am 5de758d3: am f8a01f5f: am 6468d5c2: Merge "Restart trust agents when updated or when they are dead" into lmp-dev

* commit '5de758d3868b036f5067ad9f86ee7b02331e4818':
  Restart trust agents when updated or when they are dead
diff --git a/packages/Keyguard/test/SampleTrustAgent/AndroidManifest.xml b/packages/Keyguard/test/SampleTrustAgent/AndroidManifest.xml
index f3125f1..a7c9105 100644
--- a/packages/Keyguard/test/SampleTrustAgent/AndroidManifest.xml
+++ b/packages/Keyguard/test/SampleTrustAgent/AndroidManifest.xml
@@ -38,6 +38,10 @@
           android:label="@string/app_name"
           android:exported="true"
           android:launchMode="singleInstance" >
+          <intent-filter>
+              <action android:name="android.intent.action.MAIN" />
+              <category android:name="android.intent.category.LAUNCHER" />
+          </intent-filter>
       </activity>
     </application>
 </manifest>
diff --git a/packages/Keyguard/test/SampleTrustAgent/res/layout/sample_trust_agent_settings.xml b/packages/Keyguard/test/SampleTrustAgent/res/layout/sample_trust_agent_settings.xml
index 01b107b..06a06bf 100644
--- a/packages/Keyguard/test/SampleTrustAgent/res/layout/sample_trust_agent_settings.xml
+++ b/packages/Keyguard/test/SampleTrustAgent/res/layout/sample_trust_agent_settings.xml
@@ -17,9 +17,9 @@
   -->
 
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-              android:orientation="vertical"
-              android:layout_width="match_parent"
-              android:layout_height="match_parent">
+        android:orientation="vertical"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
     <Button android:id="@+id/enable_trust"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
@@ -28,6 +28,10 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:text="Revoke trust" />
+    <Button android:id="@+id/crash"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="Crash" />
     <CheckBox android:id="@+id/report_unlock_attempts"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
diff --git a/packages/Keyguard/test/SampleTrustAgent/src/com/android/trustagent/test/SampleTrustAgentSettings.java b/packages/Keyguard/test/SampleTrustAgent/src/com/android/trustagent/test/SampleTrustAgentSettings.java
index 8e293fb..6b5f78b 100644
--- a/packages/Keyguard/test/SampleTrustAgent/src/com/android/trustagent/test/SampleTrustAgentSettings.java
+++ b/packages/Keyguard/test/SampleTrustAgent/src/com/android/trustagent/test/SampleTrustAgentSettings.java
@@ -37,6 +37,7 @@
 
         findViewById(R.id.enable_trust).setOnClickListener(this);
         findViewById(R.id.revoke_trust).setOnClickListener(this);
+        findViewById(R.id.crash).setOnClickListener(this);
 
         mReportUnlockAttempts = (CheckBox) findViewById(R.id.report_unlock_attempts);
         mReportUnlockAttempts.setOnCheckedChangeListener(this);
@@ -56,6 +57,8 @@
                     null /* extra */);
         } else if (id == R.id.revoke_trust) {
             SampleTrustAgent.sendRevokeTrust(this);
+        } else if (id == R.id.crash) {
+            throw new RuntimeException("crash");
         }
     }
 
diff --git a/services/core/java/com/android/server/trust/TrustAgentWrapper.java b/services/core/java/com/android/server/trust/TrustAgentWrapper.java
index f18939f..51009af 100644
--- a/services/core/java/com/android/server/trust/TrustAgentWrapper.java
+++ b/services/core/java/com/android/server/trust/TrustAgentWrapper.java
@@ -24,6 +24,7 @@
 import android.os.IBinder;
 import android.os.Message;
 import android.os.RemoteException;
+import android.os.SystemClock;
 import android.os.UserHandle;
 import android.util.Log;
 import android.util.Slog;
@@ -41,6 +42,13 @@
     private static final int MSG_GRANT_TRUST = 1;
     private static final int MSG_REVOKE_TRUST = 2;
     private static final int MSG_TRUST_TIMEOUT = 3;
+    private static final int MSG_RESTART_TIMEOUT = 4;
+
+    /**
+     * Time in uptime millis that we wait for the service connection, both when starting
+     * and when the service disconnects.
+     */
+    private static final long RESTART_TIMEOUT_MILLIS = 5 * 60000;
 
     /**
      * Long extra for {@link #MSG_GRANT_TRUST}
@@ -53,6 +61,8 @@
     private final ComponentName mName;
 
     private ITrustAgentService mTrustAgentService;
+    private boolean mBound;
+    private long mScheduledRestartUptimeMillis;
 
     // Trust state
     private boolean mTrusted;
@@ -95,6 +105,10 @@
                     }
                     mTrustManagerService.updateTrust(mUserId);
                     break;
+                case MSG_RESTART_TIMEOUT:
+                    unbind();
+                    mTrustManagerService.resetAgent(mName, mUserId);
+                    break;
             }
         }
     };
@@ -123,6 +137,7 @@
         @Override
         public void onServiceConnected(ComponentName name, IBinder service) {
             if (DEBUG) Log.v(TAG, "TrustAgent started : " + name.flattenToString());
+            mHandler.removeMessages(MSG_RESTART_TIMEOUT);
             mTrustAgentService = ITrustAgentService.Stub.asInterface(service);
             mTrustManagerService.mArchive.logAgentConnected(mUserId, name);
             setCallback(mCallback);
@@ -134,6 +149,9 @@
             mTrustAgentService = null;
             mTrustManagerService.mArchive.logAgentDied(mUserId, name);
             mHandler.sendEmptyMessage(MSG_REVOKE_TRUST);
+            if (mBound) {
+                scheduleRestart();
+            }
         }
     };
 
@@ -144,9 +162,12 @@
         mTrustManagerService = trustManagerService;
         mUserId = user.getIdentifier();
         mName = intent.getComponent();
-        if (!context.bindServiceAsUser(intent, mConnection, Context.BIND_AUTO_CREATE, user)) {
-            if (DEBUG) Log.v(TAG, "can't bind to TrustAgent " + mName.flattenToShortString());
-            // TODO: retry somehow?
+        // Schedules a restart for when connecting times out. If the connection succeeds,
+        // the restart is canceled in mCallback's onConnected.
+        scheduleRestart();
+        mBound = context.bindServiceAsUser(intent, mConnection, Context.BIND_AUTO_CREATE, user);
+        if (!mBound) {
+            Log.e(TAG, "Can't bind to TrustAgent " + mName.flattenToShortString());
         }
     }
 
@@ -184,14 +205,38 @@
     }
 
     public void unbind() {
+        if (!mBound) {
+            return;
+        }
         if (DEBUG) Log.v(TAG, "TrustAgent unbound : " + mName.flattenToShortString());
         mTrustManagerService.mArchive.logAgentStopped(mUserId, mName);
         mContext.unbindService(mConnection);
+        mBound = false;
         mTrustAgentService = null;
         mHandler.sendEmptyMessage(MSG_REVOKE_TRUST);
+        mHandler.removeMessages(MSG_RESTART_TIMEOUT);
     }
 
     public boolean isConnected() {
         return mTrustAgentService != null;
     }
+
+    public boolean isBound() {
+        return mBound;
+    }
+
+    /**
+     * If not connected, returns the time at which the agent is restarted.
+     *
+     * @return restart time in uptime millis.
+     */
+    public long getScheduledRestartUptimeMillis() {
+        return mScheduledRestartUptimeMillis;
+    }
+
+    private void scheduleRestart() {
+        mHandler.removeMessages(MSG_RESTART_TIMEOUT);
+        mScheduledRestartUptimeMillis = SystemClock.uptimeMillis() + RESTART_TIMEOUT_MILLIS;
+        mHandler.sendEmptyMessageAtTime(MSG_RESTART_TIMEOUT, mScheduledRestartUptimeMillis);
+    }
 }
diff --git a/services/core/java/com/android/server/trust/TrustArchive.java b/services/core/java/com/android/server/trust/TrustArchive.java
index 56950d2..5e32d86 100644
--- a/services/core/java/com/android/server/trust/TrustArchive.java
+++ b/services/core/java/com/android/server/trust/TrustArchive.java
@@ -130,7 +130,7 @@
         }
     }
 
-    private static String formatDuration(long duration) {
+    public static String formatDuration(long duration) {
         StringBuilder sb = new StringBuilder();
         TimeUtils.formatDuration(duration, sb);
         return sb.toString();
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index 60a8090..14436aa 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 {