Fix issue #2643754: Launcher is caching widget layouts for too long

With the .apk file names now changing during an update, we need
to make sure to flush all caches related to a package when the
package is removed.  Otherwise we can continue to use the old
package, since its old file may still exist if we try to load it
too soon.

Change-Id: I15f08dffca3feac999dbca4f24bef12a30ca0a66
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index fd84859..773c344 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -194,6 +194,7 @@
             }
             WeakReference<Resources> wr = mActiveResources.get(key);
             r = wr != null ? wr.get() : null;
+            //if (r != null) Slog.i(TAG, "isUpToDate " + resDir + ": " + r.getAssets().isUpToDate());
             if (r != null && r.getAssets().isUpToDate()) {
                 if (false) {
                     Slog.w(TAG, "Returning cached resources " + r + " " + resDir
@@ -1752,6 +1753,10 @@
             Debug.getMemoryInfo(outInfo);
         }
 
+        public void dispatchPackageBroadcast(int cmd, String[] packages) {
+            queueOrSendMessage(H.DISPATCH_PACKAGE_BROADCAST, packages, cmd);
+        }
+        
         @Override
         protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
             long nativeMax = Debug.getNativeHeapSize() / 1024;
@@ -1976,6 +1981,7 @@
         public static final int SUICIDE                 = 130;
         public static final int REMOVE_PROVIDER         = 131;
         public static final int ENABLE_JIT              = 132;
+        public static final int DISPATCH_PACKAGE_BROADCAST = 133;
         String codeToString(int code) {
             if (localLOGV) {
                 switch (code) {
@@ -2012,6 +2018,7 @@
                     case SUICIDE: return "SUICIDE";
                     case REMOVE_PROVIDER: return "REMOVE_PROVIDER";
                     case ENABLE_JIT: return "ENABLE_JIT";
+                    case DISPATCH_PACKAGE_BROADCAST: return "DISPATCH_PACKAGE_BROADCAST";
                 }
             }
             return "(unknown)";
@@ -2132,6 +2139,9 @@
                 case ENABLE_JIT:
                     ensureJitEnabled();
                     break;
+                case DISPATCH_PACKAGE_BROADCAST:
+                    handleDispatchPackageBroadcast(msg.arg1, (String[])msg.obj);
+                    break;
             }
         }
 
@@ -2239,16 +2249,16 @@
     // XXX For now we keep around information about all packages we have
     // seen, not removing entries from this map.
     final HashMap<String, WeakReference<PackageInfo>> mPackages
-        = new HashMap<String, WeakReference<PackageInfo>>();
+            = new HashMap<String, WeakReference<PackageInfo>>();
     final HashMap<String, WeakReference<PackageInfo>> mResourcePackages
-        = new HashMap<String, WeakReference<PackageInfo>>();
+            = new HashMap<String, WeakReference<PackageInfo>>();
     Display mDisplay = null;
     DisplayMetrics mDisplayMetrics = null;
-    HashMap<ResourcesKey, WeakReference<Resources> > mActiveResources
-        = new HashMap<ResourcesKey, WeakReference<Resources> >();
+    final HashMap<ResourcesKey, WeakReference<Resources> > mActiveResources
+            = new HashMap<ResourcesKey, WeakReference<Resources> >();
     final ArrayList<ActivityRecord> mRelaunchingActivities
             = new ArrayList<ActivityRecord>();
-        Configuration mPendingConfiguration = null;
+    Configuration mPendingConfiguration = null;
 
     // The lock of mProviderMap protects the following variables.
     final HashMap<String, ProviderRecord> mProviderMap
@@ -2271,6 +2281,8 @@
             }
             PackageInfo packageInfo = ref != null ? ref.get() : null;
             //Slog.i(TAG, "getPackageInfo " + packageName + ": " + packageInfo);
+            //if (packageInfo != null) Slog.i(TAG, "isUptoDate " + packageInfo.mResDir
+            //        + ": " + packageInfo.mResources.getAssets().isUpToDate());
             if (packageInfo != null && (packageInfo.mResources == null
                     || packageInfo.mResources.getAssets().isUpToDate())) {
                 if (packageInfo.isSecurityViolation()
@@ -2358,21 +2370,6 @@
         }
     }
 
-    public final boolean hasPackageInfo(String packageName) {
-        synchronized (mPackages) {
-            WeakReference<PackageInfo> ref;
-            ref = mPackages.get(packageName);
-            if (ref != null && ref.get() != null) {
-                return true;
-            }
-            ref = mResourcePackages.get(packageName);
-            if (ref != null && ref.get() != null) {
-                return true;
-            }
-            return false;
-        }
-    }
-
     ActivityThread() {
     }
 
@@ -4054,6 +4051,31 @@
         }
     }
 
+    final void handleDispatchPackageBroadcast(int cmd, String[] packages) {
+        boolean hasPkgInfo = false;
+        if (packages != null) {
+            for (int i=packages.length-1; i>=0; i--) {
+                //Slog.i(TAG, "Cleaning old package: " + packages[i]);
+                if (!hasPkgInfo) {
+                    WeakReference<PackageInfo> ref;
+                    ref = mPackages.get(packages[i]);
+                    if (ref != null && ref.get() != null) {
+                        hasPkgInfo = true;
+                    } else {
+                        ref = mResourcePackages.get(packages[i]);
+                        if (ref != null && ref.get() != null) {
+                            hasPkgInfo = true;
+                        }
+                    }
+                }
+                mPackages.remove(packages[i]);
+                mResourcePackages.remove(packages[i]);
+            }
+        }
+        ContextImpl.ApplicationPackageManager.handlePackageBroadcast(cmd, packages,
+                hasPkgInfo);
+    }
+        
     final void handleLowMemory() {
         ArrayList<ComponentCallbacks> callbacks
                 = new ArrayList<ComponentCallbacks>();
diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java
index 7cba13f..da26a78 100644
--- a/core/java/android/app/ApplicationThreadNative.java
+++ b/core/java/android/app/ApplicationThreadNative.java
@@ -393,6 +393,15 @@
             mi.writeToParcel(reply, 0);
             return true;
         }
+
+        case DISPATCH_PACKAGE_BROADCAST_TRANSACTION:
+        {
+            data.enforceInterface(IApplicationThread.descriptor);
+            int cmd = data.readInt();
+            String[] packages = data.readStringArray();
+            dispatchPackageBroadcast(cmd, packages);
+            return true;
+        }
         }
 
         return super.onTransact(code, data, reply, flags);
@@ -806,5 +815,16 @@
         data.recycle();
         reply.recycle();
     }
+    
+    public void dispatchPackageBroadcast(int cmd, String[] packages) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        data.writeInterfaceToken(IApplicationThread.descriptor);
+        data.writeInt(cmd);
+        data.writeStringArray(packages);
+        mRemote.transact(DISPATCH_PACKAGE_BROADCAST_TRANSACTION, data, null,
+                IBinder.FLAG_ONEWAY);
+        data.recycle();
+        
+    }
 }
 
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index fd0edaa..f471f57 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2244,33 +2244,7 @@
             return null;
         }
 
-        private void establishPackageRemovedReceiver() {
-            // mContext.registerReceiverInternal() winds up acquiring the
-            // main ActivityManagerService.this lock.  If we hold our usual
-            // sSync global lock at the same time, we impose a required ordering
-            // on those two locks, which is not good for deadlock prevention.
-            // Use a dedicated lock around initialization of
-            // sPackageRemovedReceiver to avoid this.
-            synchronized (sPackageRemovedSync) {
-                if (sPackageRemovedReceiver == null) {
-                    sPackageRemovedReceiver = new PackageRemovedReceiver();
-                    IntentFilter filter = new IntentFilter(
-                            Intent.ACTION_PACKAGE_REMOVED);
-                    filter.addDataScheme("package");
-                    mContext.registerReceiverInternal(sPackageRemovedReceiver,
-                            filter, null, null, null);
-                    // Register for events related to sdcard installation.
-                    IntentFilter sdFilter = new IntentFilter();
-                    sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
-                    mContext.registerReceiverInternal(sPackageRemovedReceiver,
-                            sdFilter, null, null, null);
-                }
-            }
-        }
-        
         private void putCachedIcon(ResourceName name, Drawable dr) {
-            establishPackageRemovedReceiver();
-
             synchronized (sSync) {
                 sIconCache.put(name, new WeakReference<Drawable>(dr));
                 if (DEBUG_ICONS) Log.v(TAG, "Added cached drawable for "
@@ -2278,29 +2252,17 @@
             }
         }
 
-        private static final class PackageRemovedReceiver extends BroadcastReceiver {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                String pkgList[] = null;
-                String action = intent.getAction();
-                boolean immediateGc = false;
-                if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
-                    pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
-                    immediateGc = true;
-                } else {
-                    Uri data = intent.getData();
-                    if (data != null) {
-                        String ssp = data.getSchemeSpecificPart();
-                        if (ssp != null) {
-                            pkgList = new String[] { ssp };
-                        }
-                    }
-                }
-                if (pkgList != null && (pkgList.length > 0)) {
-                    boolean needCleanup = false;
-                    boolean hasPkgInfo = false;
-                    for (String ssp : pkgList) {
-                        synchronized (sSync) {
+        static final void handlePackageBroadcast(int cmd, String[] pkgList,
+                boolean hasPkgInfo) {
+            boolean immediateGc = false;
+            if (cmd == IApplicationThread.EXTERNAL_STORAGE_UNAVAILABLE) {
+                immediateGc = true;
+            }
+            if (pkgList != null && (pkgList.length > 0)) {
+                boolean needCleanup = false;
+                for (String ssp : pkgList) {
+                    synchronized (sSync) {
+                        if (sIconCache.size() > 0) {
                             Iterator<ResourceName> it = sIconCache.keySet().iterator();
                             while (it.hasNext()) {
                                 ResourceName nm = it.next();
@@ -2310,7 +2272,9 @@
                                     needCleanup = true;
                                 }
                             }
-                            it = sStringCache.keySet().iterator();
+                        }
+                        if (sStringCache.size() > 0) {
+                            Iterator<ResourceName> it = sStringCache.keySet().iterator();
                             while (it.hasNext()) {
                                 ResourceName nm = it.next();
                                 if (nm.packageName.equals(ssp)) {
@@ -2320,22 +2284,19 @@
                                 }
                             }
                         }
-                        if (!hasPkgInfo) {
-                            hasPkgInfo = ActivityThread.currentActivityThread().hasPackageInfo(ssp);
-                        }
                     }
-                    if (needCleanup || hasPkgInfo) {
-                        if (immediateGc) {
-                            // Schedule an immediate gc.
-                            Runtime.getRuntime().gc();
-                        } else {
-                            ActivityThread.currentActivityThread().scheduleGcIdler();
-                        }
+                }
+                if (needCleanup || hasPkgInfo) {
+                    if (immediateGc) {
+                        // Schedule an immediate gc.
+                        Runtime.getRuntime().gc();
+                    } else {
+                        ActivityThread.currentActivityThread().scheduleGcIdler();
                     }
                 }
             }
         }
-
+        
         private static final class ResourceName {
             final String packageName;
             final int iconId;
@@ -2400,8 +2361,6 @@
         }
 
         private void putCachedString(ResourceName name, CharSequence cs) {
-            establishPackageRemovedReceiver();
-
             synchronized (sSync) {
                 sStringCache.put(name, new WeakReference<CharSequence>(cs));
             }
@@ -2665,8 +2624,6 @@
         private final IPackageManager mPM;
 
         private static final Object sSync = new Object();
-        private static final Object sPackageRemovedSync = new Object();
-        private static BroadcastReceiver sPackageRemovedReceiver;
         private static HashMap<ResourceName, WeakReference<Drawable> > sIconCache
                 = new HashMap<ResourceName, WeakReference<Drawable> >();
         private static HashMap<ResourceName, WeakReference<CharSequence> > sStringCache
diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java
index ed810d3..c917e81 100644
--- a/core/java/android/app/IApplicationThread.java
+++ b/core/java/android/app/IApplicationThread.java
@@ -100,6 +100,9 @@
             throws RemoteException;
     void setSchedulingGroup(int group) throws RemoteException;
     void getMemoryInfo(Debug.MemoryInfo outInfo) throws RemoteException;
+    static final int PACKAGE_REMOVED = 0;
+    static final int EXTERNAL_STORAGE_UNAVAILABLE = 1;
+    void dispatchPackageBroadcast(int cmd, String[] packages) throws RemoteException;
     
     String descriptor = "android.app.IApplicationThread";
 
@@ -135,4 +138,5 @@
     int SCHEDULE_DESTROY_BACKUP_AGENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+30;
     int GET_MEMORY_INFO_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+31;
     int SCHEDULE_SUICIDE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+32;
+    int DISPATCH_PACKAGE_BROADCAST_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+33;
 }
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 8383ca3..706e15a 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -12269,6 +12269,18 @@
         }
     }
     
+    private final void sendPackageBroadcastLocked(int cmd, String[] packages) {
+        for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
+            ProcessRecord r = mLruProcesses.get(i);
+            if (r.thread != null) {
+                try {
+                    r.thread.dispatchPackageBroadcast(cmd, packages);
+                } catch (RemoteException ex) {
+                }
+            }
+        }
+    }
+    
     private final int broadcastIntentLocked(ProcessRecord callerApp,
             String callerPackage, Intent intent, String resolvedType,
             IIntentReceiver resultTo, int resultCode, String resultData,
@@ -12315,6 +12327,8 @@
                             for (String pkg : list) {
                                 forceStopPackageLocked(pkg, -1, false, true, true);
                             }
+                            sendPackageBroadcastLocked(
+                                    IApplicationThread.EXTERNAL_STORAGE_UNAVAILABLE, list);
                         }
                     } else {
                         Uri data = intent.getData();
@@ -12324,6 +12338,10 @@
                                 forceStopPackageLocked(ssp,
                                         intent.getIntExtra(Intent.EXTRA_UID, -1), false, true, true);
                             }
+                            if (intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {
+                                sendPackageBroadcastLocked(IApplicationThread.PACKAGE_REMOVED,
+                                        new String[] {ssp});
+                            }
                         }
                     }
                 }