Update TrustAgentService API after review.

Also documents timebase on related DevicePolicyManager API.

Fixes bugs 16401527 and 17046034

Change-Id: I8ee6d0055ab6f52ec71630344d3232f7875d9c1d
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 0dc8f66..41bbb87 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -1373,6 +1373,8 @@
      * and its profiles or a particular one.
      * @param admin The name of the admin component to check, or null to aggregate
      * all admins.
+     * @return time in milliseconds for the given admin or the minimum value (strictest) of
+     * all admins if admin is null.
      */
     public long getMaximumTimeToLock(ComponentName admin) {
         return getMaximumTimeToLock(admin, UserHandle.myUserId());
diff --git a/core/java/android/service/trust/ITrustAgentService.aidl b/core/java/android/service/trust/ITrustAgentService.aidl
index 637d080..bd80a3f 100644
--- a/core/java/android/service/trust/ITrustAgentService.aidl
+++ b/core/java/android/service/trust/ITrustAgentService.aidl
@@ -24,6 +24,7 @@
  */
 interface ITrustAgentService {
     oneway void onUnlockAttempt(boolean successful);
+    oneway void onTrustTimeout();
     oneway void setCallback(ITrustAgentServiceCallback callback);
     oneway void setTrustAgentFeaturesEnabled(in Bundle options, IBinder token);
 }
diff --git a/core/java/android/service/trust/TrustAgentService.java b/core/java/android/service/trust/TrustAgentService.java
index 5fe9194..337ae60 100644
--- a/core/java/android/service/trust/TrustAgentService.java
+++ b/core/java/android/service/trust/TrustAgentService.java
@@ -30,6 +30,7 @@
 import android.os.IBinder;
 import android.os.Message;
 import android.os.RemoteException;
+import android.os.SystemClock;
 import android.util.Log;
 import android.util.Slog;
 
@@ -37,7 +38,10 @@
  * A service that notifies the system about whether it believes the environment of the device
  * to be trusted.
  *
- * <p>Trust agents may only be provided by the platform.</p>
+ * <p>Trust agents may only be provided by the platform. It is expected that there is only
+ * one trust agent installed on the platform. In the event there is more than one,
+ * either trust agent can enable trust.
+ * </p>
  *
  * <p>To extend this class, you must declare the service in your manifest file with
  * the {@link android.Manifest.permission#BIND_TRUST_AGENT} permission
@@ -90,6 +94,7 @@
 
     private static final int MSG_UNLOCK_ATTEMPT = 1;
     private static final int MSG_SET_TRUST_AGENT_FEATURES_ENABLED = 2;
+    private static final int MSG_TRUST_TIMEOUT = 3;
 
     private ITrustAgentServiceCallback mCallback;
 
@@ -118,6 +123,9 @@
                         onError("calling onSetTrustAgentFeaturesEnabledCompleted()");
                     }
                     break;
+                case MSG_TRUST_TIMEOUT:
+                    onTrustTimeout();
+                    break;
             }
         }
     };
@@ -139,21 +147,32 @@
     }
 
     /**
-     * Called when the user attempted to authenticate on the device.
+     * Called after the user attempts to authenticate in keyguard with their device credentials,
+     * such as pin, pattern or password.
      *
-     * @param successful true if the attempt succeeded
+     * @param successful true if the user successfully completed the challenge.
      */
     public void onUnlockAttempt(boolean successful) {
     }
 
+    /**
+     * Called when the timeout provided by the agent expires.  Note that this may be called earlier
+     * than requested by the agent if the trust timeout is adjusted by the system or
+     * {@link DevicePolicyManager}.  The agent is expected to re-evaluate the trust state and only
+     * call {@link #grantTrust(CharSequence, long, boolean)} if the trust state should be
+     * continued.
+     */
+    public void onTrustTimeout() {
+    }
+
     private void onError(String msg) {
         Slog.v(TAG, "Remote exception while " + msg);
     }
 
     /**
-     * Called when device policy wants to restrict features in the TrustAgent in response to
+     * Called when device policy wants to restrict features in the agent in response to
      * {@link DevicePolicyManager#setTrustAgentFeaturesEnabled(ComponentName, ComponentName, java.util.List) }.
-     * TrustAgents that support this feature should overload this method and return 'true'.
+     * Agents that support this feature should overload this method and return 'true'.
      *
      * The list of options can be obtained by calling
      * options.getStringArrayList({@link #KEY_FEATURES}). Presence of a feature string in the list
@@ -174,10 +193,19 @@
      * Call to grant trust on the device.
      *
      * @param message describes why the device is trusted, e.g. "Trusted by location".
-     * @param durationMs amount of time in milliseconds to keep the device in a trusted state. Trust
-     *                   for this agent will automatically be revoked when the timeout expires.
-     * @param initiatedByUser indicates that the user has explicitly initiated an action that proves
-     *                        the user is about to use the device.
+     * @param durationMs amount of time in milliseconds to keep the device in a trusted state.
+     *    Trust for this agent will automatically be revoked when the timeout expires unless
+     *    extended by a subsequent call to this function. The timeout is measured from the
+     *    invocation of this function as dictated by {@link SystemClock#elapsedRealtime())}.
+     *    For security reasons, the value should be no larger than necessary.
+     *    The value may be adjusted by the system as necessary to comply with a policy controlled
+     *    by the system or {@link DevicePolicyManager} restrictions. See {@link #onTrustTimeout()}
+     *    for determining when trust expires.
+     * @param initiatedByUser this is a hint to the system that trust is being granted as the
+     *    direct result of user action - such as solving a security challenge. The hint is used
+     *    by the system to optimize the experience. Behavior may vary by device and release, so
+     *    one should only set this parameter if it meets the above criteria rather than relying on
+     *    the behavior of any particular device or release.
      * @throws IllegalStateException if the agent is not currently managing trust.
      */
     public final void grantTrust(
@@ -254,13 +282,17 @@
     }
 
     private final class TrustAgentServiceWrapper extends ITrustAgentService.Stub {
-        @Override
+        @Override /* Binder API */
         public void onUnlockAttempt(boolean successful) {
-            mHandler.obtainMessage(MSG_UNLOCK_ATTEMPT, successful ? 1 : 0, 0)
-                    .sendToTarget();
+            mHandler.obtainMessage(MSG_UNLOCK_ATTEMPT, successful ? 1 : 0, 0).sendToTarget();
         }
 
-        @Override
+        @Override /* Binder API */
+        public void onTrustTimeout() {
+            mHandler.sendEmptyMessage(MSG_TRUST_TIMEOUT);
+        }
+
+        @Override /* Binder API */
         public void setCallback(ITrustAgentServiceCallback callback) {
             synchronized (mLock) {
                 mCallback = callback;
@@ -280,7 +312,7 @@
             }
         }
 
-        @Override
+        @Override /* Binder API */
         public void setTrustAgentFeaturesEnabled(Bundle features, IBinder token) {
             Message msg = mHandler.obtainMessage(MSG_SET_TRUST_AGENT_FEATURES_ENABLED, token);
             msg.setData(features);
diff --git a/packages/Keyguard/test/SampleTrustAgent/src/com/android/trustagent/test/SampleTrustAgent.java b/packages/Keyguard/test/SampleTrustAgent/src/com/android/trustagent/test/SampleTrustAgent.java
index 4ea1c77..c650a0f 100644
--- a/packages/Keyguard/test/SampleTrustAgent/src/com/android/trustagent/test/SampleTrustAgent.java
+++ b/packages/Keyguard/test/SampleTrustAgent/src/com/android/trustagent/test/SampleTrustAgent.java
@@ -76,6 +76,12 @@
     }
 
     @Override
+    public void onTrustTimeout() {
+        super.onTrustTimeout();
+        Toast.makeText(this, "onTrustTimeout(): timeout expired", Toast.LENGTH_SHORT).show();
+    }
+
+    @Override
     public void onUnlockAttempt(boolean successful) {
         if (getReportUnlockAttempts(this)) {
             Toast.makeText(this, "onUnlockAttempt(successful=" + successful + ")",
diff --git a/services/core/java/com/android/server/trust/TrustAgentWrapper.java b/services/core/java/com/android/server/trust/TrustAgentWrapper.java
index 0523af7..4c080cb 100644
--- a/services/core/java/com/android/server/trust/TrustAgentWrapper.java
+++ b/services/core/java/com/android/server/trust/TrustAgentWrapper.java
@@ -16,16 +16,22 @@
 
 package com.android.server.trust;
 
+import android.app.AlarmManager;
+import android.app.PendingIntent;
 import android.app.admin.DevicePolicyManager;
+import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.ServiceConnection;
+import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Message;
+import android.os.PatternMatcher;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.UserHandle;
@@ -43,6 +49,9 @@
  * TrustManager and the actual TrustAgent.
  */
 public class TrustAgentWrapper {
+    private static final String EXTRA_COMPONENT_NAME = "componentName";
+    private static final String TRUST_EXPIRED_ACTION = "android.server.trust.TRUST_EXPIRED_ACTION";
+    private static final String PERMISSION = "android.permission.PROVIDE_TRUST_AGENT";
     private static final boolean DEBUG = false;
     private static final String TAG = "TrustAgentWrapper";
 
@@ -79,6 +88,20 @@
     private boolean mTrustDisabledByDpm;
     private boolean mManagingTrust;
     private IBinder mSetTrustAgentFeaturesToken;
+    private AlarmManager mAlarmManager;
+    private final Intent mAlarmIntent;
+
+    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            ComponentName component = intent.getParcelableExtra(EXTRA_COMPONENT_NAME);
+            if (TRUST_EXPIRED_ACTION.equals(intent.getAction())
+                    && mName.equals(component)) {
+                mHandler.removeMessages(MSG_TRUST_TIMEOUT);
+                mHandler.sendEmptyMessage(MSG_TRUST_TIMEOUT);
+            }
+        }
+    };
 
     private final Handler mHandler = new Handler() {
         @Override
@@ -95,8 +118,10 @@
                     boolean initiatedByUser = msg.arg1 != 0;
                     long durationMs = msg.getData().getLong(DATA_DURATION);
                     if (durationMs > 0) {
-                        mHandler.removeMessages(MSG_TRUST_TIMEOUT);
-                        mHandler.sendEmptyMessageDelayed(MSG_TRUST_TIMEOUT, durationMs);
+                        long expiration = SystemClock.elapsedRealtime() + durationMs;
+                        PendingIntent op = PendingIntent.getBroadcast(mContext, 0, mAlarmIntent,
+                                PendingIntent.FLAG_CANCEL_CURRENT);
+                        mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, expiration, op);
                     }
                     mTrustManagerService.mArchive.logGrantTrust(mUserId, mName,
                             (mMessage != null ? mMessage.toString() : null),
@@ -106,6 +131,7 @@
                 case MSG_TRUST_TIMEOUT:
                     if (DEBUG) Slog.v(TAG, "Trust timed out : " + mName.flattenToShortString());
                     mTrustManagerService.mArchive.logTrustTimeout(mUserId, mName);
+                    onTrustTimeout();
                     // Fall through.
                 case MSG_REVOKE_TRUST:
                     mTrusted = false;
@@ -212,8 +238,19 @@
             Intent intent, UserHandle user) {
         mContext = context;
         mTrustManagerService = trustManagerService;
+        mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
         mUserId = user.getIdentifier();
         mName = intent.getComponent();
+
+        mAlarmIntent = new Intent(TRUST_EXPIRED_ACTION).putExtra(EXTRA_COMPONENT_NAME, mName);
+        mAlarmIntent.setData(Uri.parse(mAlarmIntent.toUri(Intent.URI_INTENT_SCHEME)));
+
+        final IntentFilter alarmFilter = new IntentFilter(TRUST_EXPIRED_ACTION);
+        alarmFilter.addDataScheme(mAlarmIntent.getScheme());
+        final String pathUri = mAlarmIntent.toUri(Intent.URI_INTENT_SCHEME);
+        alarmFilter.addDataPath(pathUri, PatternMatcher.PATTERN_LITERAL);
+        mContext.registerReceiver(mBroadcastReceiver, alarmFilter);
+
         // Schedules a restart for when connecting times out. If the connection succeeds,
         // the restart is canceled in mCallback's onConnected.
         scheduleRestart();
@@ -227,6 +264,13 @@
         Slog.w(TAG , "Remote Exception", e);
     }
 
+    private void onTrustTimeout() {
+        try {
+            if (mTrustAgentService != null) mTrustAgentService.onTrustTimeout();
+        } catch (RemoteException e) {
+            onError(e);
+        }
+    }
     /**
      * @see android.service.trust.TrustAgentService#onUnlockAttempt(boolean)
      */