1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.google.android.startop.iorap; 18 // TODO: rename to com.android.server.startop.iorap 19 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.content.ComponentName; 23 import android.content.Context; 24 import android.content.Intent; 25 import android.content.pm.ActivityInfo; 26 import android.os.IBinder; 27 import android.os.IBinder.DeathRecipient; 28 import android.os.Handler; 29 import android.os.Parcel; 30 import android.os.RemoteException; 31 import android.os.ServiceManager; 32 import android.os.SystemProperties; 33 import android.util.Log; 34 35 import com.android.internal.annotations.VisibleForTesting; 36 import com.android.server.IoThread; 37 import com.android.server.LocalServices; 38 import com.android.server.SystemService; 39 import com.android.server.wm.ActivityMetricsLaunchObserver; 40 import com.android.server.wm.ActivityMetricsLaunchObserver.ActivityRecordProto; 41 import com.android.server.wm.ActivityMetricsLaunchObserver.Temperature; 42 import com.android.server.wm.ActivityMetricsLaunchObserverRegistry; 43 import com.android.server.wm.ActivityTaskManagerInternal; 44 45 /** 46 * System-server-local proxy into the {@code IIorap} native service. 47 */ 48 public class IorapForwardingService extends SystemService { 49 50 public static final String TAG = "IorapForwardingService"; 51 /** $> adb shell 'setprop log.tag.IorapdForwardingService VERBOSE' */ 52 public static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 53 /** $> adb shell 'setprop ro.iorapd.enable true' */ 54 private static boolean IS_ENABLED = SystemProperties.getBoolean("ro.iorapd.enable", true); 55 /** $> adb shell 'setprop iorapd.forwarding_service.wtf_crash true' */ 56 private static boolean WTF_CRASH = SystemProperties.getBoolean( 57 "iorapd.forwarding_service.wtf_crash", false); 58 59 private IIorap mIorapRemote; 60 private final Object mLock = new Object(); 61 /** Handle onBinderDeath by periodically trying to reconnect. */ 62 private final Handler mHandler = 63 new BinderConnectionHandler(IoThread.getHandler().getLooper()); 64 65 /** 66 * Initializes the system service. 67 * <p> 68 * Subclasses must define a single argument constructor that accepts the context 69 * and passes it to super. 70 * </p> 71 * 72 * @param context The system server context. 73 */ IorapForwardingService(Context context)74 public IorapForwardingService(Context context) { 75 super(context); 76 } 77 78 //<editor-fold desc="Providers"> 79 /* 80 * Providers for external dependencies: 81 * - These are marked as protected to allow tests to inject different values via mocks. 82 */ 83 84 @VisibleForTesting provideLaunchObserverRegistry()85 protected ActivityMetricsLaunchObserverRegistry provideLaunchObserverRegistry() { 86 ActivityTaskManagerInternal amtInternal = 87 LocalServices.getService(ActivityTaskManagerInternal.class); 88 ActivityMetricsLaunchObserverRegistry launchObserverRegistry = 89 amtInternal.getLaunchObserverRegistry(); 90 return launchObserverRegistry; 91 } 92 93 @VisibleForTesting provideIorapRemote()94 protected IIorap provideIorapRemote() { 95 IIorap iorap; 96 try { 97 iorap = IIorap.Stub.asInterface(ServiceManager.getServiceOrThrow("iorapd")); 98 } catch (ServiceManager.ServiceNotFoundException e) { 99 handleRemoteError(e); 100 return null; 101 } 102 103 try { 104 iorap.asBinder().linkToDeath(provideDeathRecipient(), /*flags*/0); 105 } catch (RemoteException e) { 106 handleRemoteError(e); 107 return null; 108 } 109 110 return iorap; 111 } 112 113 @VisibleForTesting provideDeathRecipient()114 protected DeathRecipient provideDeathRecipient() { 115 return new DeathRecipient() { 116 @Override 117 public void binderDied() { 118 Log.w(TAG, "iorapd has died"); 119 retryConnectToRemoteAndConfigure(/*attempts*/0); 120 } 121 }; 122 } 123 124 @VisibleForTesting 125 protected boolean isIorapEnabled() { 126 // Same as the property in iorapd.rc -- disabling this will mean the 'iorapd' binder process 127 // never comes up, so all binder connections will fail indefinitely. 128 return IS_ENABLED; 129 } 130 131 //</editor-fold> 132 133 @Override 134 public void onStart() { 135 if (DEBUG) { 136 Log.v(TAG, "onStart"); 137 } 138 139 retryConnectToRemoteAndConfigure(/*attempts*/0); 140 } 141 142 private class BinderConnectionHandler extends Handler { 143 public BinderConnectionHandler(android.os.Looper looper) { 144 super(looper); 145 } 146 147 public static final int MESSAGE_BINDER_CONNECT = 0; 148 149 private int mAttempts = 0; 150 151 @Override 152 public void handleMessage(android.os.Message message) { 153 switch (message.what) { 154 case MESSAGE_BINDER_CONNECT: 155 if (!retryConnectToRemoteAndConfigure(mAttempts)) { 156 mAttempts++; 157 } else { 158 mAttempts = 0; 159 } 160 break; 161 default: 162 throw new AssertionError("Unknown message: " + message.toString()); 163 } 164 } 165 } 166 167 /** 168 * Handle iorapd shutdowns and crashes, by attempting to reconnect 169 * until the service is reached again. 170 * 171 * <p>The first connection attempt is synchronous, 172 * subsequent attempts are done by posting delayed tasks to the IoThread.</p> 173 * 174 * @return true if connection succeeded now, or false if it failed now [and needs to requeue]. 175 */ 176 private boolean retryConnectToRemoteAndConfigure(int attempts) { 177 final int sleepTime = 1000; // ms 178 179 if (DEBUG) { 180 Log.v(TAG, "retryConnectToRemoteAndConfigure - attempt #" + attempts); 181 } 182 183 if (connectToRemoteAndConfigure()) { 184 return true; 185 } 186 187 // Either 'iorapd' is stuck in a crash loop (ouch!!) or we manually 188 // called 'adb shell stop iorapd' , which means this would loop until it comes back 189 // up. 190 // 191 // TODO: it would be good to get nodified of 'adb shell stop iorapd' to avoid 192 // printing this warning. 193 Log.w(TAG, "Failed to connect to iorapd, is it down? Delay for " + sleepTime); 194 195 // Use a handler instead of Thread#sleep to avoid backing up the binder thread 196 // when this is called from the death recipient callback. 197 mHandler.sendMessageDelayed( 198 mHandler.obtainMessage(BinderConnectionHandler.MESSAGE_BINDER_CONNECT), 199 sleepTime); 200 201 return false; 202 203 // Log.e(TAG, "Can't connect to iorapd - giving up after " + attempts + " attempts"); 204 } 205 206 private boolean connectToRemoteAndConfigure() { 207 synchronized (mLock) { 208 // Synchronize against any concurrent calls to this via the DeathRecipient. 209 return connectToRemoteAndConfigureLocked(); 210 } 211 } 212 213 private boolean connectToRemoteAndConfigureLocked() { 214 if (!isIorapEnabled()) { 215 if (DEBUG) { 216 Log.v(TAG, "connectToRemoteAndConfigure - iorapd is disabled, skip rest of work"); 217 } 218 // When we see that iorapd is disabled (when system server comes up), 219 // it stays disabled permanently until the next system server reset. 220 221 // TODO: consider listening to property changes as a callback, then we can 222 // be more dynamic about handling enable/disable. 223 return true; 224 } 225 226 // Connect to the native binder service. 227 mIorapRemote = provideIorapRemote(); 228 if (mIorapRemote == null) { 229 Log.e(TAG, "connectToRemoteAndConfigure - null iorap remote. check for Log.wtf?"); 230 return false; 231 } 232 invokeRemote( () -> mIorapRemote.setTaskListener(new RemoteTaskListener()) ); 233 registerInProcessListenersLocked(); 234 235 return true; 236 } 237 238 private final AppLaunchObserver mAppLaunchObserver = new AppLaunchObserver(); 239 private boolean mRegisteredListeners = false; 240 241 private void registerInProcessListenersLocked() { 242 if (mRegisteredListeners) { 243 // Listeners are registered only once (idempotent operation). 244 // 245 // Today listeners are tolerant of the remote side going away 246 // by handling remote errors. 247 // 248 // We could try to 'unregister' the listener when we get a binder disconnect, 249 // but we'd still have to handle the case of encountering synchronous errors so 250 // it really wouldn't be a win (other than having less log spew). 251 return; 252 } 253 254 // Listen to App Launch Sequence events from ActivityTaskManager, 255 // and forward them to the native binder service. 256 ActivityMetricsLaunchObserverRegistry launchObserverRegistry = 257 provideLaunchObserverRegistry(); 258 launchObserverRegistry.registerLaunchObserver(mAppLaunchObserver); 259 260 mRegisteredListeners = true; 261 } 262 263 private class AppLaunchObserver implements ActivityMetricsLaunchObserver { 264 // We add a synthetic sequence ID here to make it easier to differentiate new 265 // launch sequences on the native side. 266 private @AppLaunchEvent.SequenceId long mSequenceId = -1; 267 268 // All callbacks occur on the same background thread. Don't synchronize explicitly. 269 270 @Override 271 public void onIntentStarted(@NonNull Intent intent) { 272 // #onIntentStarted [is the only transition that] initiates a new launch sequence. 273 ++mSequenceId; 274 275 if (DEBUG) { 276 Log.v(TAG, String.format("AppLaunchObserver#onIntentStarted(%d, %s)", 277 mSequenceId, intent)); 278 } 279 280 invokeRemote(() -> 281 mIorapRemote.onAppLaunchEvent(RequestId.nextValueForSequence(), 282 new AppLaunchEvent.IntentStarted(mSequenceId, intent)) 283 ); 284 } 285 286 @Override 287 public void onIntentFailed() { 288 if (DEBUG) { 289 Log.v(TAG, String.format("AppLaunchObserver#onIntentFailed(%d)", mSequenceId)); 290 } 291 292 invokeRemote(() -> 293 mIorapRemote.onAppLaunchEvent(RequestId.nextValueForSequence(), 294 new AppLaunchEvent.IntentFailed(mSequenceId)) 295 ); 296 } 297 298 @Override 299 public void onActivityLaunched(@NonNull @ActivityRecordProto byte[] activity, 300 @Temperature int temperature) { 301 if (DEBUG) { 302 Log.v(TAG, String.format("AppLaunchObserver#onActivityLaunched(%d, %s, %d)", 303 mSequenceId, activity, temperature)); 304 } 305 306 invokeRemote(() -> 307 mIorapRemote.onAppLaunchEvent(RequestId.nextValueForSequence(), 308 new AppLaunchEvent.ActivityLaunched(mSequenceId, activity, temperature)) 309 ); 310 } 311 312 @Override 313 public void onActivityLaunchCancelled(@Nullable @ActivityRecordProto byte[] activity) { 314 if (DEBUG) { 315 Log.v(TAG, String.format("AppLaunchObserver#onActivityLaunchCancelled(%d, %s)", 316 mSequenceId, activity)); 317 } 318 319 invokeRemote(() -> 320 mIorapRemote.onAppLaunchEvent(RequestId.nextValueForSequence(), 321 new AppLaunchEvent.ActivityLaunchCancelled(mSequenceId, 322 activity))); 323 } 324 325 @Override 326 public void onActivityLaunchFinished(@NonNull @ActivityRecordProto byte[] activity) { 327 if (DEBUG) { 328 Log.v(TAG, String.format("AppLaunchObserver#onActivityLaunchFinished(%d, %s)", 329 mSequenceId, activity)); 330 } 331 332 invokeRemote(() -> 333 mIorapRemote.onAppLaunchEvent(RequestId.nextValueForSequence(), 334 new AppLaunchEvent.ActivityLaunchFinished(mSequenceId, activity)) 335 ); 336 } 337 } 338 339 private class RemoteTaskListener extends ITaskListener.Stub { 340 @Override 341 public void onProgress(RequestId requestId, TaskResult result) throws RemoteException { 342 if (DEBUG) { 343 Log.v(TAG, 344 String.format("RemoteTaskListener#onProgress(%s, %s)", requestId, result)); 345 } 346 347 // TODO: implement rest. 348 } 349 350 @Override 351 public void onComplete(RequestId requestId, TaskResult result) throws RemoteException { 352 if (DEBUG) { 353 Log.v(TAG, 354 String.format("RemoteTaskListener#onComplete(%s, %s)", requestId, result)); 355 } 356 357 // TODO: implement rest. 358 } 359 } 360 361 /** Allow passing lambdas to #invokeRemote */ 362 private interface RemoteRunnable { 363 void run() throws RemoteException; 364 } 365 366 private static void invokeRemote(RemoteRunnable r) { 367 try { 368 r.run(); 369 } catch (RemoteException e) { 370 // This could be a logic error (remote side returning error), which we need to fix. 371 // 372 // This could also be a DeadObjectException in which case its probably just iorapd 373 // being manually restarted. 374 // 375 // Don't make any assumption, since DeadObjectException could also mean iorapd crashed 376 // unexpectedly. 377 // 378 // DeadObjectExceptions are recovered from using DeathRecipient and #linkToDeath. 379 handleRemoteError(e); 380 } 381 } 382 383 private static void handleRemoteError(Throwable t) { 384 if (WTF_CRASH) { 385 // In development modes, we just want to crash. 386 throw new AssertionError("unexpected remote error", t); 387 } else { 388 // Log to wtf which gets sent to dropbox, and in system_server this does not crash. 389 Log.wtf(TAG, t); 390 } 391 } 392 } 393