Show SD unavailable icon for apps on SD when ejected.

This change include a minor refactoring of PackageItemInfo and related
classes to eliminate code duplication and to avoid redundant work
searching for an ApplicationInfo instance we already have.

Bug: b/2537578
Change-Id: Id0794c3f055ea58b943028f7a84abc7dec9d0aac
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 74cfbfa..b07b690 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2148,31 +2148,7 @@
         }
 
         @Override public Drawable getApplicationIcon(ApplicationInfo info) {
-            final int icon = info.icon;
-            if (icon != 0) {
-                ResourceName name = new ResourceName(info, icon);
-                Drawable dr = getCachedIcon(name);
-                if (dr != null) {
-                    return dr;
-                }
-                try {
-                    Resources r = getResourcesForApplication(info);
-                    dr = r.getDrawable(icon);
-                    if (DEBUG_ICONS) Log.v(TAG, "Getting drawable 0x"
-                            + Integer.toHexString(icon) + " from " + r
-                            + ": " + dr);
-                    putCachedIcon(name, dr);
-                    return dr;
-                } catch (NameNotFoundException e) {
-                    Log.w("PackageManager", "Failure retrieving resources for"
-                            + info.packageName);
-                } catch (RuntimeException e) {
-                    // If an exception was thrown, fall through to return
-                    // default icon.
-                    Log.w("PackageManager", "Failure retrieving app icon", e);
-                }
-            }
-            return getDefaultActivityIcon();
+            return info.loadIcon(this);
         }
 
         @Override public Drawable getApplicationIcon(String packageName)
@@ -2413,25 +2389,6 @@
             }
         }
 
-        private CharSequence getLabel(ResourceName name, ApplicationInfo app, int id) {
-            CharSequence cs = getCachedString(name);
-            if (cs != null) {
-                return cs;
-            }
-            try {
-                Resources r = getResourcesForApplication(app);
-                cs = r.getText(id);
-                putCachedString(name, cs);
-            } catch (NameNotFoundException e) {
-                Log.w("PackageManager", "Failure retrieving resources for"
-                        + app.packageName);
-            } catch (RuntimeException e) {
-                // If an exception was thrown, fall through to return null
-                Log.w("ApplicationInfo", "Failure retrieving activity name", e);
-            }
-            return cs;
-        }
-
         @Override
         public CharSequence getText(String packageName, int resid,
                 ApplicationInfo appInfo) {
@@ -2493,17 +2450,7 @@
 
         @Override
         public CharSequence getApplicationLabel(ApplicationInfo info) {
-            if (info.nonLocalizedLabel != null) {
-                return info.nonLocalizedLabel;
-            }
-            final int id = info.labelRes;
-            if (id != 0) {
-                CharSequence cs = getLabel(new ResourceName(info, id), info, id);
-                if (cs != null) {
-                    return cs;
-                }
-            }
-            return info.packageName;
+            return info.loadLabel(this);
         }
 
         @Override
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 480504d..0a04e5b 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -16,6 +16,9 @@
 
 package android.content.pm;
 
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.Printer;
@@ -496,7 +499,7 @@
      */
     public CharSequence loadDescription(PackageManager pm) {
         if (descriptionRes != 0) {
-            CharSequence label = pm.getText(packageName, descriptionRes, null);
+            CharSequence label = pm.getText(packageName, descriptionRes, this);
             if (label != null) {
                 return label;
             }
@@ -514,4 +517,31 @@
                 FLAG_SUPPORTS_SMALL_SCREENS | FLAG_RESIZEABLE_FOR_SCREENS |
                 FLAG_SUPPORTS_SCREEN_DENSITIES);
     }
+    
+    /**
+     * @hide
+     */
+    @Override protected Drawable loadDefaultIcon(PackageManager pm) {
+        if ((flags & FLAG_EXTERNAL_STORAGE) != 0
+                && isPackageUnavailable(pm)) {
+            return Resources.getSystem().getDrawable(
+                    com.android.internal.R.drawable.sym_app_on_sd_unavailable_icon);
+        }
+        return pm.getDefaultActivityIcon();
+    }
+    
+    private boolean isPackageUnavailable(PackageManager pm) {
+        try {
+            return pm.getPackageInfo(packageName, 0) == null;
+        } catch (NameNotFoundException ex) {
+            return true;
+        }
+    }
+    
+    /**
+     * @hide
+     */
+    @Override protected ApplicationInfo getApplicationInfo() {
+        return this;
+    }
 }
diff --git a/core/java/android/content/pm/ComponentInfo.java b/core/java/android/content/pm/ComponentInfo.java
index 338c62b6..cafe372 100644
--- a/core/java/android/content/pm/ComponentInfo.java
+++ b/core/java/android/content/pm/ComponentInfo.java
@@ -99,24 +99,6 @@
         return name;
     }
     
-    @Override public Drawable loadIcon(PackageManager pm) {
-        ApplicationInfo ai = applicationInfo;
-        Drawable dr;
-        if (icon != 0) {
-            dr = pm.getDrawable(packageName, icon, ai);
-            if (dr != null) {
-                return dr;
-            }
-        }
-        if (ai.icon != 0) {
-            dr = pm.getDrawable(packageName, ai.icon, ai);
-            if (dr != null) {
-                return dr;
-            }
-        }
-        return pm.getDefaultActivityIcon();
-    }
-    
     /**
      * Return the icon resource identifier to use for this component.  If
      * the component defines an icon, that is used; else, the application
@@ -155,7 +137,7 @@
         dest.writeInt(enabled ? 1 : 0);
         dest.writeInt(exported ? 1 : 0);
     }
-
+    
     protected ComponentInfo(Parcel source) {
         super(source);
         applicationInfo = ApplicationInfo.CREATOR.createFromParcel(source);
@@ -164,4 +146,18 @@
         enabled = (source.readInt() != 0);
         exported = (source.readInt() != 0);
     }
+    
+    /**
+     * @hide
+     */
+    @Override protected Drawable loadDefaultIcon(PackageManager pm) {
+        return applicationInfo.loadIcon(pm);
+    }
+    
+    /**
+     * @hide
+     */
+    @Override protected ApplicationInfo getApplicationInfo() {
+        return applicationInfo;
+    }
 }
diff --git a/core/java/android/content/pm/PackageItemInfo.java b/core/java/android/content/pm/PackageItemInfo.java
index d666263..14c06800 100644
--- a/core/java/android/content/pm/PackageItemInfo.java
+++ b/core/java/android/content/pm/PackageItemInfo.java
@@ -103,7 +103,7 @@
             return nonLocalizedLabel;
         }
         if (labelRes != 0) {
-            CharSequence label = pm.getText(packageName, labelRes, null);
+            CharSequence label = pm.getText(packageName, labelRes, getApplicationInfo());
             if (label != null) {
                 return label.toString().trim();
             }
@@ -123,15 +123,31 @@
      * the PackageManager from which you originally retrieved this item.
      * 
      * @return Returns a Drawable containing the item's icon.  If the
-     * item does not have an icon, the default activity icon is returned.
+     * item does not have an icon, the item's default icon is returned
+     * such as the default activity icon.
      */
     public Drawable loadIcon(PackageManager pm) {
         if (icon != 0) {
-            Drawable dr = pm.getDrawable(packageName, icon, null);
+            Drawable dr = pm.getDrawable(packageName, icon, getApplicationInfo());
             if (dr != null) {
                 return dr;
             }
         }
+        return loadDefaultIcon(pm);
+    }
+    
+    /**
+     * Retrieve the default graphical icon associated with this item.
+     * 
+     * @param pm A PackageManager from which the icon can be loaded; usually
+     * the PackageManager from which you originally retrieved this item.
+     * 
+     * @return Returns a Drawable containing the item's default icon
+     * such as the default activity icon.
+     * 
+     * @hide
+     */
+    protected Drawable loadDefaultIcon(PackageManager pm) {
         return pm.getDefaultActivityIcon();
     }
     
@@ -152,7 +168,7 @@
         if (metaData != null) {
             int resid = metaData.getInt(name);
             if (resid != 0) {
-                return pm.getXml(packageName, resid, null);
+                return pm.getXml(packageName, resid, getApplicationInfo());
             }
         }
         return null;
@@ -182,7 +198,7 @@
         dest.writeInt(icon);
         dest.writeBundle(metaData);
     }
-
+    
     protected PackageItemInfo(Parcel source) {
         name = source.readString();
         packageName = source.readString();
@@ -193,6 +209,18 @@
         metaData = source.readBundle();
     }
 
+    /**
+     * Get the ApplicationInfo for the application to which this item belongs,
+     * if available, otherwise returns null.
+     * 
+     * @return Returns the ApplicationInfo of this item, or null if not known.
+     * 
+     * @hide
+     */
+    protected ApplicationInfo getApplicationInfo() {
+        return null;
+    }
+
     public static class DisplayNameComparator
             implements Comparator<PackageItemInfo> {
         public DisplayNameComparator(PackageManager pm) {
diff --git a/core/java/android/content/pm/ResolveInfo.java b/core/java/android/content/pm/ResolveInfo.java
index a35940f..74e756b 100644
--- a/core/java/android/content/pm/ResolveInfo.java
+++ b/core/java/android/content/pm/ResolveInfo.java
@@ -163,8 +163,6 @@
      * item does not have an icon, the default activity icon is returned.
      */
     public Drawable loadIcon(PackageManager pm) {
-        ComponentInfo ci = activityInfo != null ? activityInfo : serviceInfo;
-        ApplicationInfo ai = ci.applicationInfo;
         Drawable dr;
         if (resolvePackageName != null && icon != 0) {
             dr = pm.getDrawable(resolvePackageName, icon, null);
@@ -172,6 +170,8 @@
                 return dr;
             }
         }
+        ComponentInfo ci = activityInfo != null ? activityInfo : serviceInfo;
+        ApplicationInfo ai = ci.applicationInfo;
         if (icon != 0) {
             dr = pm.getDrawable(ci.packageName, icon, ai);
             if (dr != null) {
diff --git a/core/res/res/drawable-hdpi/sym_app_on_sd_unavailable_icon.png b/core/res/res/drawable-hdpi/sym_app_on_sd_unavailable_icon.png
new file mode 100644
index 0000000..d915d41
--- /dev/null
+++ b/core/res/res/drawable-hdpi/sym_app_on_sd_unavailable_icon.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/sym_app_on_sd_unavailable_icon.png b/core/res/res/drawable-mdpi/sym_app_on_sd_unavailable_icon.png
new file mode 100644
index 0000000..47306683f
--- /dev/null
+++ b/core/res/res/drawable-mdpi/sym_app_on_sd_unavailable_icon.png
Binary files differ
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index 7cd058a..6f8d640 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -6278,7 +6278,7 @@
             Log.i(TAG, "Removing non-system package:"+p.packageName);
             // Kill application pre-emptively especially for apps on sd.
             killApplication(packageName, p.applicationInfo.uid);
-            ret = deleteInstalledPackageLI (p, deleteCodeAndResources, flags, outInfo);
+            ret = deleteInstalledPackageLI(p, deleteCodeAndResources, flags, outInfo);
         }
         return ret;
     }
@@ -7605,9 +7605,10 @@
         }
 
         void setFlags(int pkgFlags) {
-            this.pkgFlags = (pkgFlags & ApplicationInfo.FLAG_SYSTEM) |
-            (pkgFlags & ApplicationInfo.FLAG_FORWARD_LOCK) |
-            (pkgFlags & ApplicationInfo.FLAG_EXTERNAL_STORAGE);
+            this.pkgFlags = pkgFlags & (
+                    ApplicationInfo.FLAG_SYSTEM |
+                    ApplicationInfo.FLAG_FORWARD_LOCK |
+                    ApplicationInfo.FLAG_EXTERNAL_STORAGE);
         }
     }
 
@@ -9606,7 +9607,7 @@
                }
                // Parse package
                int parseFlags = PackageParser.PARSE_CHATTY |
-               PackageParser.PARSE_ON_SDCARD | mDefParseFlags;
+                       PackageParser.PARSE_ON_SDCARD | mDefParseFlags;
                PackageParser pp = new PackageParser(codePath);
                pp.setSeparateProcesses(mSeparateProcesses);
                final PackageParser.Package pkg = pp.parsePackage(new File(codePath),
@@ -9682,7 +9683,7 @@
                if (res) {
                    pkgList.add(pkgName);
                } else {
-                   Slog.e(TAG, "Failed to delete pkg  from sdcard : " + pkgName);
+                   Slog.e(TAG, "Failed to delete pkg from sdcard : " + pkgName);
                    failedList.add(args);
                }
            }