Persist safe MediaSession intents across reboots

Persist a MediaSession's PendingIntent across reboots so long as it
is for the same package as the MediaSession that created it.

bug:15575138
Change-Id: I34144a90d1f9c320e7509cb9c64281b48dac9140
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 77a1fa9..667d02a 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -117,7 +117,6 @@
     public void onStart() {
         publishBinderService(Context.MEDIA_SESSION_SERVICE, mSessionManagerImpl);
         Watchdog.getInstance().addMonitor(this);
-        updateUser();
         mKeyguardManager =
                 (KeyguardManager) getContext().getSystemService(Context.KEYGUARD_SERVICE);
         mAudioService = getAudioService();
@@ -125,6 +124,8 @@
         mContentResolver = getContext().getContentResolver();
         mSettingsObserver = new SettingsObserver();
         mSettingsObserver.observe();
+
+        updateUser();
     }
 
     private IAudioService getAudioService() {
@@ -501,6 +502,12 @@
         UserRecord user = mUserRecords.get(record.getUserId());
         if (receiver != null && user != null) {
             user.mLastMediaButtonReceiver = receiver;
+            ComponentName component = receiver.getIntent().getComponent();
+            if (component != null && record.getPackageName().equals(component.getPackageName())) {
+                Settings.Secure.putStringForUser(mContentResolver,
+                        Settings.System.MEDIA_BUTTON_RECEIVER, component.flattenToString(),
+                        record.getUserId());
+            }
         }
     }
 
@@ -511,14 +518,17 @@
     final class UserRecord {
         private final int mUserId;
         private final ArrayList<MediaSessionRecord> mSessions = new ArrayList<MediaSessionRecord>();
+        private final Context mContext;
         private PendingIntent mLastMediaButtonReceiver;
+        private ComponentName mRestoredMediaButtonReceiver;
 
         public UserRecord(Context context, int userId) {
+            mContext = context;
             mUserId = userId;
+            restoreMediaButtonReceiver();
         }
 
         public void startLocked() {
-            // nothing for now
         }
 
         public void stopLocked() {
@@ -548,6 +558,7 @@
             pw.println(prefix + "Record for user " + mUserId);
             String indent = prefix + "  ";
             pw.println(indent + "MediaButtonReceiver:" + mLastMediaButtonReceiver);
+            pw.println(indent + "Restored ButtonReceiver:" + mRestoredMediaButtonReceiver);
             int size = mSessions.size();
             pw.println(indent + size + " Sessions:");
             for (int i = 0; i < size; i++) {
@@ -556,6 +567,19 @@
                 pw.println(indent + mSessions.get(i).toString());
             }
         }
+
+        private void restoreMediaButtonReceiver() {
+            String receiverName = Settings.Secure.getStringForUser(mContentResolver,
+                    Settings.System.MEDIA_BUTTON_RECEIVER, UserHandle.USER_CURRENT);
+            if (!TextUtils.isEmpty(receiverName)) {
+                ComponentName eventReceiver = ComponentName.unflattenFromString(receiverName);
+                if (eventReceiver == null) {
+                    // an invalid name was persisted
+                    return;
+                }
+                mRestoredMediaButtonReceiver = eventReceiver;
+            }
+        }
     }
 
     final class SessionsListenerRecord implements IBinder.DeathRecipient {
@@ -723,8 +747,9 @@
                 synchronized (mLock) {
                     // If we don't have a media button receiver to fall back on
                     // include non-playing sessions for dispatching
-                    boolean useNotPlayingSessions = mUserRecords.get(
-                            ActivityManager.getCurrentUser()).mLastMediaButtonReceiver == null;
+                    UserRecord ur = mUserRecords.get(ActivityManager.getCurrentUser());
+                    boolean useNotPlayingSessions = ur.mLastMediaButtonReceiver == null
+                            && ur.mRestoredMediaButtonReceiver == null;
                     MediaSessionRecord session = mPriorityStack
                             .getDefaultMediaButtonSession(mCurrentUserId, useNotPlayingSessions);
                     if (isVoiceKey(keyEvent.getKeyCode())) {
@@ -929,9 +954,12 @@
                 // Launch the last PendingIntent we had with priority
                 int userId = ActivityManager.getCurrentUser();
                 UserRecord user = mUserRecords.get(userId);
-                if (user.mLastMediaButtonReceiver != null) {
+                if (user.mLastMediaButtonReceiver != null
+                        || user.mRestoredMediaButtonReceiver != null) {
                     if (DEBUG) {
-                        Log.d(TAG, "Sending media key to last known PendingIntent");
+                        Log.d(TAG, "Sending media key to last known PendingIntent "
+                                + user.mLastMediaButtonReceiver + " or restored Intent "
+                                + user.mRestoredMediaButtonReceiver);
                     }
                     if (needWakeLock) {
                         mKeyEventReceiver.aquireWakeLockLocked();
@@ -939,9 +967,15 @@
                     Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
                     mediaButtonIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
                     try {
-                        user.mLastMediaButtonReceiver.send(getContext(),
-                                needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
-                                mediaButtonIntent, mKeyEventReceiver, null);
+                        if (user.mLastMediaButtonReceiver != null) {
+                            user.mLastMediaButtonReceiver.send(getContext(),
+                                    needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
+                                    mediaButtonIntent, mKeyEventReceiver, null);
+                        } else {
+                            mediaButtonIntent.setComponent(user.mRestoredMediaButtonReceiver);
+                            getContext().sendBroadcastAsUser(mediaButtonIntent,
+                                    new UserHandle(userId));
+                        }
                     } catch (CanceledException e) {
                         Log.i(TAG, "Error sending key event to media button receiver "
                                 + user.mLastMediaButtonReceiver, e);