Pushing state persistence to a background thread

Change-Id: If776bc22c48525ba91dc7e4dd0e27de92612f2f5
diff --git a/services/java/com/android/server/AppWidgetService.java b/services/java/com/android/server/AppWidgetService.java
index 06d37dc..532b36a 100644
--- a/services/java/com/android/server/AppWidgetService.java
+++ b/services/java/com/android/server/AppWidgetService.java
@@ -26,6 +26,8 @@
 import android.content.pm.PackageManager;
 import android.os.Binder;
 import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.UserHandle;
@@ -54,13 +56,19 @@
     Locale mLocale;
     PackageManager mPackageManager;
     boolean mSafeMode;
+    private final Handler mSaveStateHandler;
 
     private final SparseArray<AppWidgetServiceImpl> mAppWidgetServices;
 
     AppWidgetService(Context context) {
         mContext = context;
+
+        HandlerThread handlerThread = new HandlerThread("AppWidgetService -- Save state");
+        handlerThread.start();
+        mSaveStateHandler = new Handler(handlerThread.getLooper());
+
         mAppWidgetServices = new SparseArray<AppWidgetServiceImpl>(5);
-        AppWidgetServiceImpl primary = new AppWidgetServiceImpl(context, 0);
+        AppWidgetServiceImpl primary = new AppWidgetServiceImpl(context, 0, mSaveStateHandler);
         mAppWidgetServices.append(0, primary);
     }
 
@@ -229,7 +237,7 @@
             if (service == null) {
                 Slog.i(TAG, "Unable to find AppWidgetServiceImpl for user " + userId + ", adding");
                 // TODO: Verify that it's a valid user
-                service = new AppWidgetServiceImpl(mContext, userId);
+                service = new AppWidgetServiceImpl(mContext, userId, mSaveStateHandler);
                 service.systemReady(mSafeMode);
                 // Assume that BOOT_COMPLETED was received, as this is a non-primary user.
                 mAppWidgetServices.append(userId, service);
diff --git a/services/java/com/android/server/AppWidgetServiceImpl.java b/services/java/com/android/server/AppWidgetServiceImpl.java
index daa82f2..a8611d14 100644
--- a/services/java/com/android/server/AppWidgetServiceImpl.java
+++ b/services/java/com/android/server/AppWidgetServiceImpl.java
@@ -41,7 +41,10 @@
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Environment;
+import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.IBinder;
+import android.os.Looper;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.SystemClock;
@@ -180,15 +183,18 @@
     boolean mStateLoaded;
     int mMaxWidgetBitmapMemory;
 
+    private final Handler mSaveStateHandler;
+
     // These are for debugging only -- widgets are going missing in some rare instances
     ArrayList<Provider> mDeletedProviders = new ArrayList<Provider>();
     ArrayList<Host> mDeletedHosts = new ArrayList<Host>();
 
-    AppWidgetServiceImpl(Context context, int userId) {
+    AppWidgetServiceImpl(Context context, int userId, Handler saveStateHandler) {
         mContext = context;
         mPm = AppGlobals.getPackageManager();
         mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
         mUserId = userId;
+        mSaveStateHandler = saveStateHandler;
         computeMaximumWidgetBitmapMemory();
     }
 
@@ -236,7 +242,7 @@
                         updateProvidersForPackageLocked(cn.getPackageName(), removedProviders);
                     }
                 }
-                saveStateLocked();
+                saveStateAsync();
             }
         }
     }
@@ -286,7 +292,7 @@
                         providersModified |= addProvidersForPackageLocked(pkgName);
                     }
                 }
-                saveStateLocked();
+                saveStateAsync();
             }
         } else {
             Bundle extras = intent.getExtras();
@@ -297,7 +303,7 @@
                     ensureStateLoadedLocked();
                     for (String pkgName : pkgList) {
                         providersModified |= removeProvidersForPackageLocked(pkgName);
-                        saveStateLocked();
+                        saveStateAsync();
                     }
                 }
             }
@@ -410,7 +416,7 @@
 
     private void ensureStateLoadedLocked() {
         if (!mStateLoaded) {
-            loadAppWidgetList();
+            loadAppWidgetListLocked();
             loadStateLocked();
             mStateLoaded = true;
         }
@@ -431,7 +437,7 @@
             host.instances.add(id);
             mAppWidgetIds.add(id);
 
-            saveStateLocked();
+            saveStateAsync();
             if (DBG) log("Allocating AppWidgetId for " + packageName + " host=" + hostId
                     + " id=" + appWidgetId);
             return appWidgetId;
@@ -444,7 +450,7 @@
             AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
             if (id != null) {
                 deleteAppWidgetLocked(id);
-                saveStateLocked();
+                saveStateAsync();
             }
         }
     }
@@ -456,7 +462,7 @@
             Host host = lookupHostLocked(callingUid, hostId);
             if (host != null) {
                 deleteHostLocked(host);
-                saveStateLocked();
+                saveStateAsync();
             }
         }
     }
@@ -475,7 +481,7 @@
                 }
             }
             if (changed) {
-                saveStateLocked();
+                saveStateAsync();
             }
         }
     }
@@ -591,7 +597,7 @@
 
                 // schedule the future updates
                 registerForBroadcastsLocked(p, getAppWidgetIds(p));
-                saveStateLocked();
+                saveStateAsync();
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
@@ -655,8 +661,8 @@
             } else {
                 mPackagesWithBindWidgetPermission.remove(packageName);
             }
+            saveStateAsync();
         }
-        saveStateLocked();
     }
 
     // Binds to a specific RemoteViewsService
@@ -893,6 +899,20 @@
         }
     }
 
+    private void saveStateAsync() {
+        mSaveStateHandler.post(mSaveStateRunnable);
+    }
+
+    private final Runnable mSaveStateRunnable = new Runnable() {
+        @Override
+        public void run() {
+            synchronized (mAppWidgetIds) {
+                ensureStateLoadedLocked();
+                saveStateLocked();
+            }
+        }
+    };
+
     public void updateAppWidgetOptions(int appWidgetId, Bundle options) {
         synchronized (mAppWidgetIds) {
             options = cloneIfLocalBinder(options);
@@ -913,7 +933,7 @@
             intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId);
             intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, id.options);
             mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId));
-            saveStateLocked();
+            saveStateAsync();
         }
     }
 
@@ -1214,7 +1234,7 @@
         }
     }
 
-    void loadAppWidgetList() {
+    void loadAppWidgetListLocked() {
         Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
         try {
             List<ResolveInfo> broadcastReceivers = mPm.queryIntentReceivers(intent,