Documentation for the Unreal C++ Plugin
Loading...
Searching...
No Matches
MXRAdminAppMessenger.java
Go to the documentation of this file.
1/*
2 * Copyright 2023 Mighty Immersion, Inc. All Rights Reserved.
3 */
4package com.managexr.getserialsample;
5
6
7import android.content.ComponentName;
8import android.content.Context;
9import android.content.Intent;
10import android.content.ServiceConnection;
11import android.content.SharedPreferences;
12import android.content.pm.PackageInfo;
13import android.content.pm.PackageManager;
14import android.os.Bundle;
15import android.os.Handler;
16import android.os.IBinder;
17import android.os.Message;
18import android.os.Messenger;
19import android.os.RemoteException;
20import android.util.Log;
21
22import androidx.annotation.NonNull;
23
24import org.apache.commons.lang3.StringEscapeUtils;
25import org.json.JSONObject;
26
27import java.util.List;
28
30 public static class AdminAppMessageTypes {
31 public static final int REGISTER_CLIENT = 0;
32 public static final int GET_DEVICE_STATUS = 5;
33 public static final int DEVICE_STATUS = 5000;
34 }
35
36 public interface BindStatusListener {
38 }
39
40 public interface SerialListener {
41 void onSerialUpdated(String serial);
42 }
43
44 private final static String TAG = "MXRAdminAppMessenger";
45 private final static String ADMIN_SERVICE_CLASS_NAME = "com.mightyimmersion.mightyplatform.AdminService";
46
47 private final Messenger incomingMessenger = new Messenger(new IncomingMessageHandler());
48 private Messenger outgoingMessenger;
49 private boolean bound;
50 private final Context context;
53
54 private final int CHECK_BINDING_FREQUENCY = 60 * 1000; // 1 minute
55 private final Handler checkBindingHandler = new Handler();
56
57 public MXRAdminAppMessenger(Context _context) {
58 context = _context;
60 }
61
67 public void onBindStatusUpdated(BindStatusListener _listener) {
68 bindStatusListener = _listener;
69 }
70
74 public void onSerialUpdated(SerialListener _listener) {
75 serialListener = _listener;
76 String knownSerial = getSerial();
77 if (knownSerial != null) serialListener.onSerialUpdated(knownSerial);
78 }
79
83 public boolean isManaged() {
85 }
86
90 public boolean isBound() {
91 return bound;
92 }
93
100 public String getSerial() {
101 return getSharedPrefs().getString("serial", null);
102 }
103
108 private void updateSerial(@NonNull String serial) {
109 String currentSerial = getSerial();
110 if (serial.equals(currentSerial)) return;
111
112 SharedPreferences prefs = getSharedPrefs();
113 SharedPreferences.Editor prefsEditor = prefs.edit();
114 prefsEditor.putString("serial", serial);
115 prefsEditor.apply();
116
118 }
119
123 private SharedPreferences getSharedPrefs() {
124 return context.getSharedPreferences(context.getPackageName() + ".managexr_prefs", Context.MODE_PRIVATE);
125 }
126
131 class IncomingMessageHandler extends Handler {
132 @Override
133 public void handleMessage(Message msg) {
134 try {
135 Bundle bundle = msg.getData();
136 if (msg.what == AdminAppMessageTypes.DEVICE_STATUS) {
137 String deviceStatusJsonString = bundle.getString("json", null);
138 if (deviceStatusJsonString == null) return;
139 JSONObject deviceStatusJson = new JSONObject(removeQuotesAndUnescape(deviceStatusJsonString));
140 String serial = deviceStatusJson.getString("serial");
141 if (serial.length() > 0) updateSerial(serial);
142
143 }
144 } catch (Exception e) {
145 Log.v(TAG, "Failed to parse json: " + e.getMessage());
146 e.printStackTrace();
147 }
148 }
149 }
150
151 private String removeQuotesAndUnescape(String uncleanJson) {
152 String noQuotes = uncleanJson.replaceAll("^\"|\"$", "");
153 return StringEscapeUtils.unescapeJava(noQuotes);
154 }
155
157 // Code below this section is for binding to and maintaining communication with the ManageXR
158 // Admin App.
160
164
165 protected void bindToAdminServiceLoop() {
167 checkBindingHandler.postDelayed(this::bindToAdminServiceLoop, CHECK_BINDING_FREQUENCY);
168 }
169
170 private void tryBindToAdminService() {
171 if (bound) return;
172 Log.v(TAG, "tryBindToAdminService");
173
174 ComponentName adminServiceComponent = getInstalledAdminAdminServiceComponent();
175 if (adminServiceComponent != null) {
176 launchAdminAppServiceIfNeeded(adminServiceComponent.getPackageName());
177 Intent bindIntent = new Intent();
178 bindIntent.setComponent(adminServiceComponent);
179 // This will bind to the service whether or not it is running. As soon as the service is
180 // started the onServiceConnected method will fire.
181 context.bindService(bindIntent, mConnection, 0);
182 } else {
183 Log.v(TAG, "ManageXR Admin App not installed or QUERY_ALL_PACKAGES permissions was not added to the manifest!");
184 }
185 }
186
187 private void unbindFromAdminService() {
188 context.unbindService(mConnection);
189 }
190
191 private final ServiceConnection mConnection = new ServiceConnection() {
192 public void onServiceConnected(ComponentName className, IBinder service) {
193 Log.v(TAG, "onServiceConnected");
194 outgoingMessenger = new Messenger(service);
195 bound = true;
196 boolean registeredAsClient = registerAsClient();
197 if (registeredAsClient) {
198 Log.v(TAG, "Registered as client");
201 } else {
202 Log.e(TAG, "Failed to register as client. Unbinding...");
204 }
205 }
206
207 public void onServiceDisconnected(ComponentName className) {
208 Log.v(TAG, "onServiceDisconnected");
209 outgoingMessenger = null;
210 bound = false;
212 }
213 };
214
215 private boolean registerAsClient() {
217 }
218
222
223 public boolean sendMessage(int what) {
224 return sendMessage(what, null);
225 }
226
227 public boolean sendMessage(int what, String jsonString) {
228 if (!bound) {
230 return false;
231 }
232
233 Message msg = Message.obtain(null, what);
234 msg.replyTo = incomingMessenger;
235
236 if (jsonString != null) {
237 Bundle bundle = new Bundle();
238 bundle.putString("json", jsonString);
239 msg.setData(bundle);
240 }
241
242 try {
243 outgoingMessenger.send(msg);
244 } catch (RemoteException e) {
245 Log.e(TAG, e.getMessage());
246 return false;
247 }
248 return true;
249 }
250
252 PackageManager pm = context.getPackageManager();
253 List<PackageInfo> packages = pm.getInstalledPackages(0);
254
255 for (PackageInfo packageInfo : packages) {
256 if (packageInfo.packageName.startsWith("com.mightyimmersion.mightyplatform.adminapp") && !packageInfo.packageName.contains("preload")) {
257 return new ComponentName(packageInfo.packageName, ADMIN_SERVICE_CLASS_NAME);
258 }
259 }
260 return null;
261 }
262
263 private void launchAdminAppServiceIfNeeded(String packageName) {
264 Intent intent = new Intent();
265 intent.setClassName(packageName, ADMIN_SERVICE_CLASS_NAME);
266 context.startForegroundService(intent);
267 }
268}
void onBindStatusUpdated(BindStatusListener _listener)