Invalidate caches when locale changes.

When loading roots for the first time, we're okay using any cached
data from the system, but if the locale changes we need to
force-refresh everything.

Now that we're always using the system cache, we have a nice strong
signal for "empty" versus "not cached" results, so we don't need to
wait around for the first loading pass to finish.

Add logic to invalidate system cache when locale changes, and fix
locking bug.

Bug: 27977906
Change-Id: Ic50083eff360bea887799583f6c9f02c129eec91
diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java
index 7f7ea9d..e9d9628 100644
--- a/services/core/java/com/android/server/content/ContentService.java
+++ b/services/core/java/com/android/server/content/ContentService.java
@@ -42,10 +42,10 @@
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
+import android.os.FactoryTest;
 import android.os.IBinder;
 import android.os.Parcel;
 import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.text.TextUtils;
@@ -59,6 +59,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.LocalServices;
+import com.android.server.SystemService;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -74,6 +75,29 @@
 public final class ContentService extends IContentService.Stub {
     private static final String TAG = "ContentService";
 
+    public static class Lifecycle extends SystemService {
+        private ContentService mContentService;
+
+        public Lifecycle(Context context) {
+            super(context);
+        }
+
+        @Override
+        public void onStart() {
+            final boolean factoryTest = (FactoryTest
+                    .getMode() == FactoryTest.FACTORY_TEST_LOW_LEVEL);
+            mContentService = new ContentService(getContext(), factoryTest);
+            publishBinderService(ContentResolver.CONTENT_SERVICE_NAME, mContentService);
+        }
+
+        @Override
+        public void onCleanupUser(int userHandle) {
+            synchronized (mContentService.mCache) {
+                mContentService.mCache.remove(userHandle);
+            }
+        }
+    }
+
     private Context mContext;
     private boolean mFactoryTest;
 
@@ -94,12 +118,18 @@
     private BroadcastReceiver mCacheReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
-            final Uri data = intent.getData();
-            if (data != null) {
-                final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
-                        UserHandle.USER_NULL);
-                final String packageName = data.getSchemeSpecificPart();
-                invalidateCacheLocked(userId, packageName, null);
+            synchronized (mCache) {
+                if (Intent.ACTION_LOCALE_CHANGED.equals(intent.getAction())) {
+                    mCache.clear();
+                } else {
+                    final Uri data = intent.getData();
+                    if (data != null) {
+                        final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
+                                UserHandle.USER_NULL);
+                        final String packageName = data.getSchemeSpecificPart();
+                        invalidateCacheLocked(userId, packageName, null);
+                    }
+                }
             }
         }
     };
@@ -227,6 +257,11 @@
         packageFilter.addDataScheme("package");
         mContext.registerReceiverAsUser(mCacheReceiver, UserHandle.ALL,
                 packageFilter, null, null);
+
+        final IntentFilter localeFilter = new IntentFilter();
+        localeFilter.addAction(Intent.ACTION_LOCALE_CHANGED);
+        mContext.registerReceiverAsUser(mCacheReceiver, UserHandle.ALL,
+                localeFilter, null, null);
     }
 
     public void systemReady() {
@@ -1080,12 +1115,6 @@
         }
     }
 
-    public static ContentService main(Context context, boolean factoryTest) {
-        ContentService service = new ContentService(context, factoryTest);
-        ServiceManager.addService(ContentResolver.CONTENT_SERVICE_NAME, service);
-        return service;
-    }
-
     /**
      * Checks if the request is from the system or an app that has INTERACT_ACROSS_USERS_FULL
      * permission, if the userHandle is not for the caller.
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 659450e..81e43fd 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -157,6 +157,8 @@
             "com.google.android.clockwork.ThermalObserver";
     private static final String WEAR_BLUETOOTH_SERVICE_CLASS =
             "com.google.android.clockwork.bluetooth.WearBluetoothService";
+    private static final String CONTENT_SERVICE_CLASS =
+            "com.android.server.content.ContentService$Lifecycle";
 
     private static final String PERSISTENT_DATA_BLOCK_PROP = "ro.frp.pst";
 
@@ -574,8 +576,7 @@
             Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
 
             traceBeginAndSlog("StartContentService");
-            contentService = ContentService.main(context,
-                    mFactoryTestMode == FactoryTest.FACTORY_TEST_LOW_LEVEL);
+            mSystemServiceManager.startService(CONTENT_SERVICE_CLASS);
             Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
 
             traceBeginAndSlog("InstallSystemProviders");
@@ -1324,7 +1325,7 @@
                     reportWtf("starting System UI", e);
                 }
                 Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
-                Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "MakeMountServiceReady");
+                Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "MakeNetworkScoreReady");
                 try {
                     if (networkScoreF != null) networkScoreF.systemReady();
                 } catch (Throwable e) {