Loading services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java +134 −3 Original line number Diff line number Diff line Loading @@ -28,13 +28,17 @@ import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ParceledListSlice; import android.content.pm.ServiceInfo; import android.os.IBinder; import android.os.RemoteException; import android.service.appprediction.AppPredictionService; import android.util.ArrayMap; import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.server.infra.AbstractPerUserSystemService; import java.util.ArrayList; /** * Per-user instance of {@link AppPredictionManagerService}. */ Loading @@ -48,6 +52,17 @@ public class AppPredictionPerUserService extends @GuardedBy("mLock") private RemoteAppPredictionService mRemoteService; /** * When {@code true}, remote service died but service state is kept so it's restored after * the system re-binds to it. */ @GuardedBy("mLock") private boolean mZombie; @GuardedBy("mLock") private final ArrayMap<AppPredictionSessionId, AppPredictionSessionInfo> mSessionInfos = new ArrayMap<>(); protected AppPredictionPerUserService(AppPredictionManagerService master, Object lock, int userId) { super(master, lock, userId); Loading Loading @@ -92,6 +107,16 @@ public class AppPredictionPerUserService extends final RemoteAppPredictionService service = getRemoteServiceLocked(); if (service != null) { service.onCreatePredictionSession(context, sessionId); mSessionInfos.put(sessionId, new AppPredictionSessionInfo(sessionId, context, () -> { synchronized (mLock) { AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId); if (sessionInfo != null) { sessionInfo.removeAllCallbacksLocked(); mSessionInfos.remove(sessionId); } } })); } } Loading Loading @@ -140,6 +165,11 @@ public class AppPredictionPerUserService extends final RemoteAppPredictionService service = getRemoteServiceLocked(); if (service != null) { service.registerPredictionUpdates(sessionId, callback); AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId); if (sessionInfo != null) { sessionInfo.addCallbackLocked(callback); } } } Loading @@ -152,6 +182,11 @@ public class AppPredictionPerUserService extends final RemoteAppPredictionService service = getRemoteServiceLocked(); if (service != null) { service.unregisterPredictionUpdates(sessionId, callback); AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId); if (sessionInfo != null) { sessionInfo.removeCallbackLocked(callback); } } } Loading @@ -174,6 +209,12 @@ public class AppPredictionPerUserService extends final RemoteAppPredictionService service = getRemoteServiceLocked(); if (service != null) { service.onDestroyPredictionSession(sessionId); AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId); if (sessionInfo != null) { sessionInfo.removeAllCallbacksLocked(); mSessionInfos.remove(sessionId); } } } Loading @@ -182,17 +223,54 @@ public class AppPredictionPerUserService extends if (isDebug()) { Slog.d(TAG, "onFailureOrTimeout(): timed out=" + timedOut); } // Do nothing, we are just proxying to the prediction service } @Override public void onConnectedStateChanged(boolean connected) { if (isDebug()) { Slog.d(TAG, "onConnectedStateChanged(): connected=" + connected); } if (connected) { synchronized (mLock) { if (mZombie) { // Sanity check - shouldn't happen if (mRemoteService == null) { Slog.w(TAG, "Cannot resurrect sessions because remote service is null"); return; } mZombie = false; resurrectSessionsLocked(); } } } } @Override public void onServiceDied(RemoteAppPredictionService service) { if (isDebug()) { Slog.d(TAG, "onServiceDied():"); Slog.w(TAG, "onServiceDied(): service=" + service); } synchronized (mLock) { mZombie = true; } // Do nothing, eventually the system will bind to the remote service again... } // Do nothing, we are just proxying to the prediction service /** * Called after the remote service connected, it's used to restore state from a 'zombie' * service (i.e., after it died). */ private void resurrectSessionsLocked() { final int numSessions = mSessionInfos.size(); if (isDebug()) { Slog.d(TAG, "Resurrecting remote service (" + mRemoteService + ") on " + numSessions + " sessions."); } for (AppPredictionSessionInfo sessionInfo : mSessionInfos.values()) { sessionInfo.resurrectSessionLocked(this); } } @GuardedBy("mLock") Loading @@ -215,4 +293,57 @@ public class AppPredictionPerUserService extends return mRemoteService; } private static final class AppPredictionSessionInfo { private final AppPredictionSessionId mSessionId; private final AppPredictionContext mContext; private final ArrayList<IPredictionCallback> mCallbacks = new ArrayList<>(); private final IBinder.DeathRecipient mBinderDeathHandler; AppPredictionSessionInfo(AppPredictionSessionId id, AppPredictionContext context, IBinder.DeathRecipient binderDeathHandler) { mSessionId = id; mContext = context; mBinderDeathHandler = binderDeathHandler; } void addCallbackLocked(IPredictionCallback callback) { if (mBinderDeathHandler != null) { try { callback.asBinder().linkToDeath(mBinderDeathHandler, 0); } catch (RemoteException e) { Slog.e(TAG, "Failed to link to death: " + e); } } mCallbacks.add(callback); } void removeCallbackLocked(IPredictionCallback callback) { if (mBinderDeathHandler != null) { callback.asBinder().unlinkToDeath(mBinderDeathHandler, 0); } mCallbacks.remove(callback); } void removeAllCallbacksLocked() { if (mBinderDeathHandler != null) { for (IPredictionCallback callback : mCallbacks) { callback.asBinder().unlinkToDeath(mBinderDeathHandler, 0); } } mCallbacks.clear(); } void resurrectSessionLocked(AppPredictionPerUserService service) { if (service.isDebug()) { Slog.d(TAG, "Resurrecting remote service (" + service.getRemoteServiceLocked() + ") for session Id=" + mSessionId + " and " + mCallbacks.size() + " callbacks."); } service.onCreatePredictionSessionLocked(mContext, mSessionId); for (IPredictionCallback callback : mCallbacks) { service.registerPredictionUpdatesLocked(mSessionId, callback); } } } } services/appprediction/java/com/android/server/appprediction/RemoteAppPredictionService.java +15 −0 Original line number Diff line number Diff line Loading @@ -42,6 +42,8 @@ public class RemoteAppPredictionService extends private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 2 * DateUtils.SECOND_IN_MILLIS; private final RemoteAppPredictionServiceCallbacks mCallback; public RemoteAppPredictionService(Context context, String serviceInterface, ComponentName componentName, int userId, RemoteAppPredictionServiceCallbacks callback, boolean bindInstantServiceAllowed, Loading @@ -50,6 +52,7 @@ public class RemoteAppPredictionService extends context.getMainThreadHandler(), bindInstantServiceAllowed ? Context.BIND_ALLOW_INSTANT : 0, verbose, /* initialCapacity= */ 1); mCallback = callback; } @Override Loading Loading @@ -141,5 +144,17 @@ public class RemoteAppPredictionService extends * Notifies a the failure or timeout of a remote call. */ void onFailureOrTimeout(boolean timedOut); /** * Notifies change in connected state of the remote service. */ void onConnectedStateChanged(boolean connected); } @Override // from AbstractRemoteService protected void handleOnConnectedStateChanged(boolean connected) { if (mCallback != null) { mCallback.onConnectedStateChanged(connected); } } } Loading
services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java +134 −3 Original line number Diff line number Diff line Loading @@ -28,13 +28,17 @@ import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ParceledListSlice; import android.content.pm.ServiceInfo; import android.os.IBinder; import android.os.RemoteException; import android.service.appprediction.AppPredictionService; import android.util.ArrayMap; import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.server.infra.AbstractPerUserSystemService; import java.util.ArrayList; /** * Per-user instance of {@link AppPredictionManagerService}. */ Loading @@ -48,6 +52,17 @@ public class AppPredictionPerUserService extends @GuardedBy("mLock") private RemoteAppPredictionService mRemoteService; /** * When {@code true}, remote service died but service state is kept so it's restored after * the system re-binds to it. */ @GuardedBy("mLock") private boolean mZombie; @GuardedBy("mLock") private final ArrayMap<AppPredictionSessionId, AppPredictionSessionInfo> mSessionInfos = new ArrayMap<>(); protected AppPredictionPerUserService(AppPredictionManagerService master, Object lock, int userId) { super(master, lock, userId); Loading Loading @@ -92,6 +107,16 @@ public class AppPredictionPerUserService extends final RemoteAppPredictionService service = getRemoteServiceLocked(); if (service != null) { service.onCreatePredictionSession(context, sessionId); mSessionInfos.put(sessionId, new AppPredictionSessionInfo(sessionId, context, () -> { synchronized (mLock) { AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId); if (sessionInfo != null) { sessionInfo.removeAllCallbacksLocked(); mSessionInfos.remove(sessionId); } } })); } } Loading Loading @@ -140,6 +165,11 @@ public class AppPredictionPerUserService extends final RemoteAppPredictionService service = getRemoteServiceLocked(); if (service != null) { service.registerPredictionUpdates(sessionId, callback); AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId); if (sessionInfo != null) { sessionInfo.addCallbackLocked(callback); } } } Loading @@ -152,6 +182,11 @@ public class AppPredictionPerUserService extends final RemoteAppPredictionService service = getRemoteServiceLocked(); if (service != null) { service.unregisterPredictionUpdates(sessionId, callback); AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId); if (sessionInfo != null) { sessionInfo.removeCallbackLocked(callback); } } } Loading @@ -174,6 +209,12 @@ public class AppPredictionPerUserService extends final RemoteAppPredictionService service = getRemoteServiceLocked(); if (service != null) { service.onDestroyPredictionSession(sessionId); AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId); if (sessionInfo != null) { sessionInfo.removeAllCallbacksLocked(); mSessionInfos.remove(sessionId); } } } Loading @@ -182,17 +223,54 @@ public class AppPredictionPerUserService extends if (isDebug()) { Slog.d(TAG, "onFailureOrTimeout(): timed out=" + timedOut); } // Do nothing, we are just proxying to the prediction service } @Override public void onConnectedStateChanged(boolean connected) { if (isDebug()) { Slog.d(TAG, "onConnectedStateChanged(): connected=" + connected); } if (connected) { synchronized (mLock) { if (mZombie) { // Sanity check - shouldn't happen if (mRemoteService == null) { Slog.w(TAG, "Cannot resurrect sessions because remote service is null"); return; } mZombie = false; resurrectSessionsLocked(); } } } } @Override public void onServiceDied(RemoteAppPredictionService service) { if (isDebug()) { Slog.d(TAG, "onServiceDied():"); Slog.w(TAG, "onServiceDied(): service=" + service); } synchronized (mLock) { mZombie = true; } // Do nothing, eventually the system will bind to the remote service again... } // Do nothing, we are just proxying to the prediction service /** * Called after the remote service connected, it's used to restore state from a 'zombie' * service (i.e., after it died). */ private void resurrectSessionsLocked() { final int numSessions = mSessionInfos.size(); if (isDebug()) { Slog.d(TAG, "Resurrecting remote service (" + mRemoteService + ") on " + numSessions + " sessions."); } for (AppPredictionSessionInfo sessionInfo : mSessionInfos.values()) { sessionInfo.resurrectSessionLocked(this); } } @GuardedBy("mLock") Loading @@ -215,4 +293,57 @@ public class AppPredictionPerUserService extends return mRemoteService; } private static final class AppPredictionSessionInfo { private final AppPredictionSessionId mSessionId; private final AppPredictionContext mContext; private final ArrayList<IPredictionCallback> mCallbacks = new ArrayList<>(); private final IBinder.DeathRecipient mBinderDeathHandler; AppPredictionSessionInfo(AppPredictionSessionId id, AppPredictionContext context, IBinder.DeathRecipient binderDeathHandler) { mSessionId = id; mContext = context; mBinderDeathHandler = binderDeathHandler; } void addCallbackLocked(IPredictionCallback callback) { if (mBinderDeathHandler != null) { try { callback.asBinder().linkToDeath(mBinderDeathHandler, 0); } catch (RemoteException e) { Slog.e(TAG, "Failed to link to death: " + e); } } mCallbacks.add(callback); } void removeCallbackLocked(IPredictionCallback callback) { if (mBinderDeathHandler != null) { callback.asBinder().unlinkToDeath(mBinderDeathHandler, 0); } mCallbacks.remove(callback); } void removeAllCallbacksLocked() { if (mBinderDeathHandler != null) { for (IPredictionCallback callback : mCallbacks) { callback.asBinder().unlinkToDeath(mBinderDeathHandler, 0); } } mCallbacks.clear(); } void resurrectSessionLocked(AppPredictionPerUserService service) { if (service.isDebug()) { Slog.d(TAG, "Resurrecting remote service (" + service.getRemoteServiceLocked() + ") for session Id=" + mSessionId + " and " + mCallbacks.size() + " callbacks."); } service.onCreatePredictionSessionLocked(mContext, mSessionId); for (IPredictionCallback callback : mCallbacks) { service.registerPredictionUpdatesLocked(mSessionId, callback); } } } }
services/appprediction/java/com/android/server/appprediction/RemoteAppPredictionService.java +15 −0 Original line number Diff line number Diff line Loading @@ -42,6 +42,8 @@ public class RemoteAppPredictionService extends private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 2 * DateUtils.SECOND_IN_MILLIS; private final RemoteAppPredictionServiceCallbacks mCallback; public RemoteAppPredictionService(Context context, String serviceInterface, ComponentName componentName, int userId, RemoteAppPredictionServiceCallbacks callback, boolean bindInstantServiceAllowed, Loading @@ -50,6 +52,7 @@ public class RemoteAppPredictionService extends context.getMainThreadHandler(), bindInstantServiceAllowed ? Context.BIND_ALLOW_INSTANT : 0, verbose, /* initialCapacity= */ 1); mCallback = callback; } @Override Loading Loading @@ -141,5 +144,17 @@ public class RemoteAppPredictionService extends * Notifies a the failure or timeout of a remote call. */ void onFailureOrTimeout(boolean timedOut); /** * Notifies change in connected state of the remote service. */ void onConnectedStateChanged(boolean connected); } @Override // from AbstractRemoteService protected void handleOnConnectedStateChanged(boolean connected) { if (mCallback != null) { mCallback.onConnectedStateChanged(connected); } } }