Give ServiceWatcher a proper lifecycle

ServiceWatcher currently starts working once registered and can never be
unregistered.

Test: manual
Change-Id: I3c193d0c75eafb8e1934663f8928797396b723ba
diff --git a/services/core/java/com/android/server/ServiceWatcher.java b/services/core/java/com/android/server/ServiceWatcher.java
index b78b5d9..3ccb6e5 100644
--- a/services/core/java/com/android/server/ServiceWatcher.java
+++ b/services/core/java/com/android/server/ServiceWatcher.java
@@ -185,9 +185,49 @@
     private final Handler mHandler;
     private final Intent mIntent;
 
+    private final PackageMonitor mPackageMonitor = new PackageMonitor() {
+        @Override
+        public boolean onPackageChanged(String packageName, int uid, String[] components) {
+            return true;
+        }
+
+        @Override
+        public void onSomePackagesChanged() {
+            onBestServiceChanged(false);
+        }
+    };
+    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (action == null) {
+                return;
+            }
+            int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
+            if (userId == UserHandle.USER_NULL) {
+                return;
+            }
+
+            switch (action) {
+                case Intent.ACTION_USER_SWITCHED:
+                    onUserSwitched(userId);
+                    break;
+                case Intent.ACTION_USER_UNLOCKED:
+                    onUserUnlocked(userId);
+                    break;
+                default:
+                    break;
+            }
+
+        }
+    };
+
     @Nullable private final OnBindRunner mOnBind;
     @Nullable private final Runnable mOnUnbind;
 
+    // write from caller thread only, read anywhere
+    private volatile boolean mRegistered;
+
     // read/write from handler thread only
     private int mCurrentUserId;
 
@@ -225,77 +265,65 @@
     }
 
     /**
-     * Register this class, which will start the process of determining the best matching service
-     * and maintaining a binding to it. Will return false and fail if there are no possible matching
-     * services at the time this functions is called.
+     * Returns true if there is at least one component that could satisfy the ServiceWatcher's
+     * constraints.
      */
-    public boolean register() {
-        if (mContext.getPackageManager().queryIntentServicesAsUser(mIntent,
+    public boolean checkServiceResolves() {
+        return !mContext.getPackageManager().queryIntentServicesAsUser(mIntent,
                 MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE | MATCH_SYSTEM_ONLY,
-                UserHandle.USER_SYSTEM).isEmpty()) {
-            return false;
-        }
+                UserHandle.USER_SYSTEM).isEmpty();
+    }
 
-        new PackageMonitor() {
-            @Override
-            public boolean onPackageChanged(String packageName, int uid, String[] components) {
-                return true;
-            }
+    /**
+     * Starts the process of determining the best matching service and maintaining a binding to it.
+     */
+    public void register() {
+        Preconditions.checkState(!mRegistered);
 
-            @Override
-            public void onSomePackagesChanged() {
-                onBestServiceChanged(false);
-            }
-        }.register(mContext, UserHandle.ALL, true, mHandler);
+        mPackageMonitor.register(mContext, UserHandle.ALL, true, mHandler);
 
         IntentFilter intentFilter = new IntentFilter();
         intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
         intentFilter.addAction(Intent.ACTION_USER_UNLOCKED);
-        mContext.registerReceiverAsUser(new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                String action = intent.getAction();
-                if (action == null) {
-                    return;
-                }
-                int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
-                if (userId == UserHandle.USER_NULL) {
-                    return;
-                }
-
-                switch (action) {
-                    case Intent.ACTION_USER_SWITCHED:
-                        onUserSwitched(userId);
-                        break;
-                    case Intent.ACTION_USER_UNLOCKED:
-                        onUserUnlocked(userId);
-                        break;
-                    default:
-                        break;
-                }
-
-            }
-        }, UserHandle.ALL, intentFilter, null, mHandler);
+        mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, intentFilter, null,
+                mHandler);
 
         mCurrentUserId = ActivityManager.getCurrentUser();
 
+        mRegistered = true;
+
         mHandler.post(() -> onBestServiceChanged(false));
-        return true;
+    }
+
+    /**
+     * Stops the process of determining the best matching service and releases any binding.
+     */
+    public void unregister() {
+        Preconditions.checkState(mRegistered);
+
+        mRegistered = false;
+
+        mPackageMonitor.unregister();
+        mContext.unregisterReceiver(mBroadcastReceiver);
+
+        mHandler.post(() -> onBestServiceChanged(false));
     }
 
     private void onBestServiceChanged(boolean forceRebind) {
         Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
 
-        List<ResolveInfo> resolveInfos = mContext.getPackageManager().queryIntentServicesAsUser(
-                mIntent,
-                GET_META_DATA | MATCH_DIRECT_BOOT_AUTO | MATCH_SYSTEM_ONLY,
-                mCurrentUserId);
-
         ServiceInfo bestServiceInfo = ServiceInfo.NONE;
-        for (ResolveInfo resolveInfo : resolveInfos) {
-            ServiceInfo serviceInfo = new ServiceInfo(resolveInfo, mCurrentUserId);
-            if (serviceInfo.compareTo(bestServiceInfo) > 0) {
-                bestServiceInfo = serviceInfo;
+
+        if (mRegistered) {
+            List<ResolveInfo> resolveInfos = mContext.getPackageManager().queryIntentServicesAsUser(
+                    mIntent,
+                    GET_META_DATA | MATCH_DIRECT_BOOT_AUTO | MATCH_SYSTEM_ONLY,
+                    mCurrentUserId);
+            for (ResolveInfo resolveInfo : resolveInfos) {
+                ServiceInfo serviceInfo = new ServiceInfo(resolveInfo, mCurrentUserId);
+                if (serviceInfo.compareTo(bestServiceInfo) > 0) {
+                    bestServiceInfo = serviceInfo;
+                }
             }
         }
 
diff --git a/services/core/java/com/android/server/location/GeocoderProxy.java b/services/core/java/com/android/server/location/GeocoderProxy.java
index 3b74d5a..3ac148d 100644
--- a/services/core/java/com/android/server/location/GeocoderProxy.java
+++ b/services/core/java/com/android/server/location/GeocoderProxy.java
@@ -60,7 +60,11 @@
     }
 
     private boolean register() {
-        return mServiceWatcher.register();
+        boolean resolves = mServiceWatcher.checkServiceResolves();
+        if (resolves) {
+            mServiceWatcher.register();
+        }
+        return resolves;
     }
 
     /**
diff --git a/services/core/java/com/android/server/location/HardwareActivityRecognitionProxy.java b/services/core/java/com/android/server/location/HardwareActivityRecognitionProxy.java
index eed1aac..7b400b6 100644
--- a/services/core/java/com/android/server/location/HardwareActivityRecognitionProxy.java
+++ b/services/core/java/com/android/server/location/HardwareActivityRecognitionProxy.java
@@ -75,7 +75,11 @@
     }
 
     private boolean register() {
-        return mServiceWatcher.register();
+        boolean resolves = mServiceWatcher.checkServiceResolves();
+        if (resolves) {
+            mServiceWatcher.register();
+        }
+        return resolves;
     }
 
     private void onBind(IBinder binder, ComponentName service) throws RemoteException {
diff --git a/services/core/java/com/android/server/location/ProxyLocationProvider.java b/services/core/java/com/android/server/location/ProxyLocationProvider.java
index ce9b10d..555b2d2 100644
--- a/services/core/java/com/android/server/location/ProxyLocationProvider.java
+++ b/services/core/java/com/android/server/location/ProxyLocationProvider.java
@@ -92,7 +92,11 @@
     }
 
     private boolean register() {
-        return mServiceWatcher.register();
+        boolean resolves = mServiceWatcher.checkServiceResolves();
+        if (resolves) {
+            mServiceWatcher.register();
+        }
+        return resolves;
     }
 
     private void onBind(IBinder binder, ComponentName service) throws RemoteException {
diff --git a/services/core/java/com/android/server/location/geofence/GeofenceProxy.java b/services/core/java/com/android/server/location/geofence/GeofenceProxy.java
index 686a66b..bdfa6d7 100644
--- a/services/core/java/com/android/server/location/geofence/GeofenceProxy.java
+++ b/services/core/java/com/android/server/location/geofence/GeofenceProxy.java
@@ -75,16 +75,16 @@
     }
 
     private boolean register(Context context) {
-        if (mServiceWatcher.register()) {
+        boolean resolves = mServiceWatcher.checkServiceResolves();
+        if (resolves) {
+            mServiceWatcher.register();
             context.bindServiceAsUser(
                     new Intent(context, GeofenceHardwareService.class),
                     new GeofenceProxyServiceConnection(),
                     Context.BIND_AUTO_CREATE,
                     UserHandle.SYSTEM);
-            return true;
         }
-
-        return false;
+        return resolves;
     }
 
     private class GeofenceProxyServiceConnection implements ServiceConnection {
diff --git a/services/core/java/com/android/server/location/timezone/RealLocationTimeZoneProviderProxy.java b/services/core/java/com/android/server/location/timezone/RealLocationTimeZoneProviderProxy.java
index 3801877..94062fa 100644
--- a/services/core/java/com/android/server/location/timezone/RealLocationTimeZoneProviderProxy.java
+++ b/services/core/java/com/android/server/location/timezone/RealLocationTimeZoneProviderProxy.java
@@ -78,10 +78,14 @@
     }
 
     private boolean register() {
-        return mServiceWatcher.register();
+        boolean resolves = mServiceWatcher.checkServiceResolves();
+        if (resolves) {
+            mServiceWatcher.register();
+        }
+        return resolves;
     }
 
-    private void onBind(IBinder binder, ComponentName componentName) throws RemoteException {
+    private void onBind(IBinder binder, ComponentName componentName) {
         processServiceWatcherCallbackOnThreadingDomainThread(() -> onBindOnHandlerThread(binder));
     }