Restart trust agents when updated or when they are dead

ActivityManager restarts the trust agent service for us
when it gets killed automatically. This does not apply
when its process crashes too often or when its package
gets updated however.
To catch the update case, the trust agent connection
is removed as soon as the package disappears, and then
readded when the new package appears.
To catch the repeated crashing case, the connection is
reset if it hasn't successfully connected for several minutes.

Also adds a button to SampleTrustAgent to simulate a crash.

Bug: 16137258
Change-Id: I1b18fc7a3025e23e25ca1623b6af658d5430a94b
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index 1aec569..6f72b94 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -45,6 +45,7 @@
 import android.os.IBinder;
 import android.os.Message;
 import android.os.RemoteException;
+import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.service.trust.TrustAgentService;
@@ -99,12 +100,6 @@
 
     private UserManager mUserManager;
 
-    /**
-     * Cache for {@link #refreshAgentList()}
-     */
-    private final ArraySet<AgentInfo> mObsoleteAgents = new ArraySet<AgentInfo>();
-
-
     public TrustManagerService(Context context) {
         super(context);
         mContext = context;
@@ -168,8 +163,8 @@
         List<UserInfo> userInfos = mUserManager.getUsers(true /* excludeDying */);
         LockPatternUtils lockPatternUtils = new LockPatternUtils(mContext);
 
-        mObsoleteAgents.clear();
-        mObsoleteAgents.addAll(mActiveAgents);
+        ArraySet<AgentInfo> obsoleteAgents = new ArraySet<>();
+        obsoleteAgents.addAll(mActiveAgents);
 
         for (UserInfo userInfo : userInfos) {
             int disabledFeatures = lockPatternUtils.getDevicePolicyManager()
@@ -208,14 +203,14 @@
                             new Intent().setComponent(name), userInfo.getUserHandle());
                     mActiveAgents.add(agentInfo);
                 } else {
-                    mObsoleteAgents.remove(agentInfo);
+                    obsoleteAgents.remove(agentInfo);
                 }
             }
         }
 
         boolean trustMayHaveChanged = false;
-        for (int i = 0; i < mObsoleteAgents.size(); i++) {
-            AgentInfo info = mObsoleteAgents.valueAt(i);
+        for (int i = 0; i < obsoleteAgents.size(); i++) {
+            AgentInfo info = obsoleteAgents.valueAt(i);
             if (info.agent.isTrusted()) {
                 trustMayHaveChanged = true;
             }
@@ -228,6 +223,43 @@
         }
     }
 
+    private void removeAgentsOfPackage(String packageName) {
+        boolean trustMayHaveChanged = false;
+        for (int i = mActiveAgents.size() - 1; i >= 0; i--) {
+            AgentInfo info = mActiveAgents.valueAt(i);
+            if (packageName.equals(info.component.getPackageName())) {
+                Log.i(TAG, "Resetting agent " + info.component.flattenToShortString());
+                if (info.agent.isTrusted()) {
+                    trustMayHaveChanged = true;
+                }
+                info.agent.unbind();
+                mActiveAgents.removeAt(i);
+            }
+        }
+        if (trustMayHaveChanged) {
+            updateTrustAll();
+        }
+    }
+
+    public void resetAgent(ComponentName name, int userId) {
+        boolean trustMayHaveChanged = false;
+        for (int i = mActiveAgents.size() - 1; i >= 0; i--) {
+            AgentInfo info = mActiveAgents.valueAt(i);
+            if (name.equals(info.component) && userId == info.userId) {
+                Log.i(TAG, "Resetting agent " + info.component.flattenToShortString());
+                if (info.agent.isTrusted()) {
+                    trustMayHaveChanged = true;
+                }
+                info.agent.unbind();
+                mActiveAgents.removeAt(i);
+            }
+        }
+        if (trustMayHaveChanged) {
+            updateTrust(userId);
+        }
+        refreshAgentList();
+    }
+
     private ComponentName getSettingsComponentName(PackageManager pm, ResolveInfo resolveInfo) {
         if (resolveInfo == null || resolveInfo.serviceInfo == null
                 || resolveInfo.serviceInfo.metaData == null) return null;
@@ -448,11 +480,18 @@
                 if (info.userId != user.id) { continue; }
                 boolean trusted = info.agent.isTrusted();
                 fout.print("    "); fout.println(info.component.flattenToShortString());
-                fout.print("     connected=" + dumpBool(info.agent.isConnected()));
+                fout.print("     bound=" + dumpBool(info.agent.isBound()));
+                fout.print(", connected=" + dumpBool(info.agent.isConnected()));
                 fout.println(", trusted=" + dumpBool(trusted));
                 if (trusted) {
                     fout.println("      message=\"" + info.agent.getMessage() + "\"");
                 }
+                if (!info.agent.isConnected()) {
+                    String restartTime = TrustArchive.formatDuration(
+                            info.agent.getScheduledRestartUptimeMillis()
+                                    - SystemClock.uptimeMillis());
+                    fout.println("      restartScheduledAt=" + restartTime);
+                }
                 if (!simpleNames.add(TrustArchive.getSimpleName(info.component))) {
                     duplicateSimpleNames = true;
                 }
@@ -501,6 +540,11 @@
             // We're interested in all changes, even if just some components get enabled / disabled.
             return true;
         }
+
+        @Override
+        public void onPackageDisappeared(String packageName, int reason) {
+            removeAgentsOfPackage(packageName);
+        }
     };
 
     private class DevicePolicyReceiver extends BroadcastReceiver {