am cacba761: Notify of the update of TvInputInfo

* commit 'cacba7615051b8667d5b2b178603ed78eb492e28':
  Notify of the update of TvInputInfo
diff --git a/media/java/android/media/tv/ITvInputManagerCallback.aidl b/media/java/android/media/tv/ITvInputManagerCallback.aidl
index 50a7636..6792680 100644
--- a/media/java/android/media/tv/ITvInputManagerCallback.aidl
+++ b/media/java/android/media/tv/ITvInputManagerCallback.aidl
@@ -24,4 +24,5 @@
     void onInputStateChanged(in String inputId, int state);
     void onInputAdded(in String inputId);
     void onInputRemoved(in String inputId);
+    void onInputUpdated(in String inputId);
 }
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index b7a230c..f809f54 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -477,6 +477,18 @@
          */
         public void onInputRemoved(String inputId) {
         }
+
+        /**
+         * This is called when a TV input is updated. The update of TV input happens when it is
+         * reinstalled or the media on which the newer version of TV input exists is
+         * available/unavailable.
+         *
+         * @param inputId The id of the TV input.
+         * @hide
+         */
+        @SystemApi
+        public void onInputUpdated(String inputId) {
+        }
     }
 
     private static final class TvInputCallbackRecord {
@@ -518,6 +530,15 @@
                 }
             });
         }
+
+        public void postInputUpdated(final String inputId) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mListener.onInputUpdated(inputId);
+                }
+            });
+        }
     }
 
     /**
@@ -707,6 +728,15 @@
                     }
                 }
             }
+
+            @Override
+            public void onInputUpdated(String inputId) {
+                synchronized (mLock) {
+                    for (TvInputListenerRecord record : mTvInputListenerRecordsList) {
+                        record.postInputUpdated(inputId);
+                    }
+                }
+            }
         };
         try {
             if (mService != null) {
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 3380f71..adae84fd 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -85,6 +85,7 @@
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -139,7 +140,7 @@
             registerBroadcastReceivers();
         } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
             synchronized (mLock) {
-                buildTvInputListLocked(mCurrentUserId);
+                buildTvInputListLocked(mCurrentUserId, null);
                 buildTvContentRatingSystemListLocked(mCurrentUserId);
             }
         }
@@ -148,19 +149,64 @@
 
     private void registerBroadcastReceivers() {
         PackageMonitor monitor = new PackageMonitor() {
+            private void buildTvInputList(String[] packages) {
+                synchronized (mLock) {
+                    buildTvInputListLocked(getChangingUserId(), packages);
+                    buildTvContentRatingSystemListLocked(getChangingUserId());
+                }
+            }
+
+            @Override
+            public void onPackageUpdateFinished(String packageName, int uid) {
+                if (DEBUG) Slog.d(TAG, "onPackageUpdateFinished(packageName=" + packageName + ")");
+                // This callback is invoked when the TV input is reinstalled.
+                // In this case, isReplacing() always returns true.
+                buildTvInputList(new String[] { packageName });
+            }
+
+            @Override
+            public void onPackagesAvailable(String[] packages) {
+                if (DEBUG) {
+                    Slog.d(TAG, "onPackagesAvailable(packages=" + Arrays.toString(packages) + ")");
+                }
+                // This callback is invoked when the media on which some packages exist become
+                // available.
+                if (isReplacing()) {
+                    buildTvInputList(packages);
+                }
+            }
+
+            @Override
+            public void onPackagesUnavailable(String[] packages) {
+                // This callback is invoked when the media on which some packages exist become
+                // unavailable.
+                if (DEBUG)  {
+                    Slog.d(TAG, "onPackagesUnavailable(packages=" + Arrays.toString(packages)
+                            + ")");
+                }
+                if (isReplacing()) {
+                    buildTvInputList(packages);
+                }
+            }
+
             @Override
             public void onSomePackagesChanged() {
+                // TODO: Use finer-grained methods(e.g. onPackageAdded, onPackageRemoved) to manage
+                // the TV inputs.
                 if (DEBUG) Slog.d(TAG, "onSomePackagesChanged()");
-                synchronized (mLock) {
-                    buildTvInputListLocked(mCurrentUserId);
-                    buildTvContentRatingSystemListLocked(mCurrentUserId);
+                if (isReplacing()) {
+                    if (DEBUG) Slog.d(TAG, "Skipped building TV input list due to replacing");
+                    // When the package is updated, buildTvInputListLocked is called in other
+                    // methods instead.
+                    return;
                 }
+                buildTvInputList(null);
             }
 
             @Override
             public void onPackageRemoved(String packageName, int uid) {
                 synchronized (mLock) {
-                    UserState userState = getUserStateLocked(mCurrentUserId);
+                    UserState userState = getUserStateLocked(getChangingUserId());
                     if (!userState.packageSet.contains(packageName)) {
                         // Not a TV input package.
                         return;
@@ -218,13 +264,11 @@
                 component.getPackageName()) == PackageManager.PERMISSION_GRANTED;
     }
 
-    private void buildTvInputListLocked(int userId) {
+    private void buildTvInputListLocked(int userId, String[] updatedPackages) {
         UserState userState = getUserStateLocked(userId);
         userState.packageSet.clear();
 
-        if (DEBUG) {
-            Slog.d(TAG, "buildTvInputList");
-        }
+        if (DEBUG) Slog.d(TAG, "buildTvInputList");
         PackageManager pm = mContext.getPackageManager();
         List<ResolveInfo> services = pm.queryIntentServices(
                 new Intent(TvInputService.SERVICE_INTERFACE),
@@ -278,6 +322,15 @@
         for (String inputId : inputMap.keySet()) {
             if (!userState.inputMap.containsKey(inputId)) {
                 notifyInputAddedLocked(userState, inputId);
+            } else if (updatedPackages != null) {
+                // Notify the package updates
+                TvInputState inputState = inputMap.get(inputId);
+                for (String updatedPackage : updatedPackages) {
+                    if (inputState.info.getComponent().getPackageName().equals(updatedPackage)) {
+                        notifyInputUpdatedLocked(userState, inputId);
+                        break;
+                    }
+                }
             }
         }
 
@@ -337,7 +390,7 @@
                 userState = new UserState(mContext, userId);
             }
             mUserStates.put(userId, userState);
-            buildTvInputListLocked(userId);
+            buildTvInputListLocked(userId, null);
             buildTvContentRatingSystemListLocked(userId);
         }
     }
@@ -649,6 +702,19 @@
         }
     }
 
+    private void notifyInputUpdatedLocked(UserState userState, String inputId) {
+        if (DEBUG) {
+            Slog.d(TAG, "notifyInputUpdatedLocked(inputId=" + inputId + ")");
+        }
+        for (ITvInputManagerCallback callback : userState.callbackSet) {
+            try {
+                callback.onInputUpdated(inputId);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "failed to report updated input to callback", e);
+            }
+        }
+    }
+
     private void notifyInputStateChangedLocked(UserState userState, String inputId,
             int state, ITvInputManagerCallback targetCallback) {
         if (DEBUG) {
@@ -1813,7 +1879,7 @@
         private void addTvInputLocked(TvInputInfo inputInfo) {
             ServiceState serviceState = getServiceStateLocked(mComponent, mUserId);
             serviceState.inputList.add(inputInfo);
-            buildTvInputListLocked(mUserId);
+            buildTvInputListLocked(mUserId, null);
         }
 
         @Override
@@ -1851,7 +1917,7 @@
                     }
                 }
                 if (removed) {
-                    buildTvInputListLocked(mUserId);
+                    buildTvInputListLocked(mUserId, null);
                     mTvInputHardwareManager.removeTvInput(inputId);
                 } else {
                     Slog.e(TAG, "failed to remove input " + inputId);