TrustAgentService.java revision bab773d23ff8dedcf5a258362c4c603da63dafe5
1/**
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.service.trust;
18
19import android.Manifest;
20import android.annotation.IntDef;
21import android.annotation.NonNull;
22import android.annotation.SdkConstant;
23import android.annotation.SystemApi;
24import android.app.Service;
25import android.app.admin.DevicePolicyManager;
26import android.content.ComponentName;
27import android.content.Context;
28import android.content.Intent;
29import android.content.pm.PackageManager;
30import android.content.pm.ServiceInfo;
31import android.os.Bundle;
32import android.os.Handler;
33import android.os.IBinder;
34import android.os.Message;
35import android.os.PersistableBundle;
36import android.os.RemoteException;
37import android.os.UserHandle;
38import android.os.UserManager;
39import android.util.Log;
40import android.util.Slog;
41
42import java.lang.annotation.Retention;
43import java.lang.annotation.RetentionPolicy;
44import java.util.List;
45
46/**
47 * A service that notifies the system about whether it believes the environment of the device
48 * to be trusted.
49 *
50 * <p>Trust agents may only be provided by the platform. It is expected that there is only
51 * one trust agent installed on the platform. In the event there is more than one,
52 * either trust agent can enable trust.
53 * </p>
54 *
55 * <p>To extend this class, you must declare the service in your manifest file with
56 * the {@link android.Manifest.permission#BIND_TRUST_AGENT} permission
57 * and include an intent filter with the {@link #SERVICE_INTERFACE} action. For example:</p>
58 * <pre>
59 * &lt;service android:name=".TrustAgent"
60 *          android:label="&#64;string/service_name"
61 *          android:permission="android.permission.BIND_TRUST_AGENT">
62 *     &lt;intent-filter>
63 *         &lt;action android:name="android.service.trust.TrustAgentService" />
64 *     &lt;/intent-filter>
65 *     &lt;meta-data android:name="android.service.trust.trustagent"
66 *          android:value="&#64;xml/trust_agent" />
67 * &lt;/service></pre>
68 *
69 * <p>The associated meta-data file can specify an activity that is accessible through Settings
70 * and should allow configuring the trust agent, as defined in
71 * {@link android.R.styleable#TrustAgent}. For example:</p>
72 *
73 * <pre>
74 * &lt;trust-agent xmlns:android="http://schemas.android.com/apk/res/android"
75 *          android:settingsActivity=".TrustAgentSettings" /></pre>
76 *
77 * @hide
78 */
79@SystemApi
80public class TrustAgentService extends Service {
81
82    private final String TAG = TrustAgentService.class.getSimpleName() +
83            "[" + getClass().getSimpleName() + "]";
84    private static final boolean DEBUG = false;
85
86    /**
87     * The {@link Intent} that must be declared as handled by the service.
88     */
89    @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
90    public static final String SERVICE_INTERFACE
91            = "android.service.trust.TrustAgentService";
92
93    /**
94     * The name of the {@code meta-data} tag pointing to additional configuration of the trust
95     * agent.
96     */
97    public static final String TRUST_AGENT_META_DATA = "android.service.trust.trustagent";
98
99
100    /**
101     * Flag for {@link #grantTrust(CharSequence, long, int)} indicating that trust is being granted
102     * as the direct result of user action - such as solving a security challenge. The hint is used
103     * by the system to optimize the experience. Behavior may vary by device and release, so
104     * one should only set this parameter if it meets the above criteria rather than relying on
105     * the behavior of any particular device or release.
106     */
107    public static final int FLAG_GRANT_TRUST_INITIATED_BY_USER = 1 << 0;
108
109    /**
110     * Flag for {@link #grantTrust(CharSequence, long, int)} indicating that the agent would like
111     * to dismiss the keyguard. When using this flag, the {@code TrustAgentService} must ensure
112     * it is only set in response to a direct user action with the expectation of dismissing the
113     * keyguard.
114     */
115    public static final int FLAG_GRANT_TRUST_DISMISS_KEYGUARD = 1 << 1;
116
117    /** @hide */
118    @Retention(RetentionPolicy.SOURCE)
119    @IntDef(flag = true, prefix = { "FLAG_GRANT_TRUST_" }, value = {
120            FLAG_GRANT_TRUST_INITIATED_BY_USER,
121            FLAG_GRANT_TRUST_DISMISS_KEYGUARD,
122    })
123    public @interface GrantTrustFlags {}
124
125
126    /**
127     * Int enum indicating that escrow token is active.
128     * See {@link #onEscrowTokenStateReceived(long, int)}
129     *
130     */
131    public static final int TOKEN_STATE_ACTIVE = 1;
132
133    /**
134     * Int enum indicating that escow token is inactive.
135     * See {@link #onEscrowTokenStateReceived(long, int)}
136     *
137     */
138    public static final int TOKEN_STATE_INACTIVE = 0;
139
140    /** @hide */
141    @Retention(RetentionPolicy.SOURCE)
142    @IntDef(flag = true, prefix = { "TOKEN_STATE_" }, value = {
143            TOKEN_STATE_ACTIVE,
144            TOKEN_STATE_INACTIVE,
145    })
146    public @interface TokenState {}
147
148    private static final int MSG_UNLOCK_ATTEMPT = 1;
149    private static final int MSG_CONFIGURE = 2;
150    private static final int MSG_TRUST_TIMEOUT = 3;
151    private static final int MSG_DEVICE_LOCKED = 4;
152    private static final int MSG_DEVICE_UNLOCKED = 5;
153    private static final int MSG_UNLOCK_LOCKOUT = 6;
154    private static final int MSG_ESCROW_TOKEN_ADDED = 7;
155    private static final int MSG_ESCROW_TOKEN_STATE_RECEIVED = 8;
156    private static final int MSG_ESCROW_TOKEN_REMOVED = 9;
157
158    private static final String EXTRA_TOKEN = "token";
159    private static final String EXTRA_TOKEN_HANDLE = "token_handle";
160    private static final String EXTRA_USER_HANDLE = "user_handle";
161    private static final String EXTRA_TOKEN_STATE = "token_state";
162    private static final String EXTRA_TOKEN_REMOVED_RESULT = "token_removed_result";
163    /**
164     * Class containing raw data for a given configuration request.
165     */
166    private static final class ConfigurationData {
167        final IBinder token;
168        final List<PersistableBundle> options;
169        ConfigurationData(List<PersistableBundle> opts, IBinder t) {
170            options = opts;
171            token = t;
172        }
173    }
174
175    private ITrustAgentServiceCallback mCallback;
176
177    private Runnable mPendingGrantTrustTask;
178
179    private boolean mManagingTrust;
180
181    // Lock used to access mPendingGrantTrustTask and mCallback.
182    private final Object mLock = new Object();
183
184    private Handler mHandler = new Handler() {
185        public void handleMessage(android.os.Message msg) {
186            switch (msg.what) {
187                case MSG_UNLOCK_ATTEMPT:
188                    onUnlockAttempt(msg.arg1 != 0);
189                    break;
190                case MSG_UNLOCK_LOCKOUT:
191                    onDeviceUnlockLockout(msg.arg1);
192                    break;
193                case MSG_CONFIGURE: {
194                    ConfigurationData data = (ConfigurationData) msg.obj;
195                    boolean result = onConfigure(data.options);
196                    if (data.token != null) {
197                        try {
198                            synchronized (mLock) {
199                                mCallback.onConfigureCompleted(result, data.token);
200                            }
201                        } catch (RemoteException e) {
202                            onError("calling onSetTrustAgentFeaturesEnabledCompleted()");
203                        }
204                    }
205                    break;
206                }
207                case MSG_TRUST_TIMEOUT:
208                    onTrustTimeout();
209                    break;
210                case MSG_DEVICE_LOCKED:
211                    onDeviceLocked();
212                    break;
213                case MSG_DEVICE_UNLOCKED:
214                    onDeviceUnlocked();
215                    break;
216                case MSG_ESCROW_TOKEN_ADDED: {
217                    Bundle data = msg.getData();
218                    byte[] token = data.getByteArray(EXTRA_TOKEN);
219                    long handle = data.getLong(EXTRA_TOKEN_HANDLE);
220                    UserHandle user = (UserHandle) data.getParcelable(EXTRA_USER_HANDLE);
221                    onEscrowTokenAdded(token, handle, user);
222                    break;
223                }
224                case MSG_ESCROW_TOKEN_STATE_RECEIVED: {
225                    Bundle data = msg.getData();
226                    long handle = data.getLong(EXTRA_TOKEN_HANDLE);
227                    int tokenState = data.getInt(EXTRA_TOKEN_STATE, TOKEN_STATE_INACTIVE);
228                    onEscrowTokenStateReceived(handle, tokenState);
229                    break;
230                }
231                case MSG_ESCROW_TOKEN_REMOVED: {
232                    Bundle data = msg.getData();
233                    long handle = data.getLong(EXTRA_TOKEN_HANDLE);
234                    boolean success = data.getBoolean(EXTRA_TOKEN_REMOVED_RESULT);
235                    onEscrowTokenRemoved(handle, success);
236                    break;
237                }
238            }
239        }
240    };
241
242    @Override
243    public void onCreate() {
244        super.onCreate();
245        ComponentName component = new ComponentName(this, getClass());
246        try {
247            ServiceInfo serviceInfo = getPackageManager().getServiceInfo(component, 0 /* flags */);
248            if (!Manifest.permission.BIND_TRUST_AGENT.equals(serviceInfo.permission)) {
249                throw new IllegalStateException(component.flattenToShortString()
250                        + " is not declared with the permission "
251                        + "\"" + Manifest.permission.BIND_TRUST_AGENT + "\"");
252            }
253        } catch (PackageManager.NameNotFoundException e) {
254            Log.e(TAG, "Can't get ServiceInfo for " + component.toShortString());
255        }
256    }
257
258    /**
259     * Called after the user attempts to authenticate in keyguard with their device credentials,
260     * such as pin, pattern or password.
261     *
262     * @param successful true if the user successfully completed the challenge.
263     */
264    public void onUnlockAttempt(boolean successful) {
265    }
266
267    /**
268     * Called when the timeout provided by the agent expires.  Note that this may be called earlier
269     * than requested by the agent if the trust timeout is adjusted by the system or
270     * {@link DevicePolicyManager}.  The agent is expected to re-evaluate the trust state and only
271     * call {@link #grantTrust(CharSequence, long, boolean)} if the trust state should be
272     * continued.
273     */
274    public void onTrustTimeout() {
275    }
276
277    /**
278     * Called when the device enters a state where a PIN, pattern or
279     * password must be entered to unlock it.
280     */
281    public void onDeviceLocked() {
282    }
283
284    /**
285     * Called when the device leaves a state where a PIN, pattern or
286     * password must be entered to unlock it.
287     */
288    public void onDeviceUnlocked() {
289    }
290
291    /**
292     * Called when the device enters a temporary unlock lockout.
293     *
294     * <p>This occurs when the user has consecutively failed to unlock the device too many times,
295     * and must wait until a timeout has passed to perform another attempt. The user may then only
296     * use strong authentication mechanisms (PIN, pattern or password) to unlock the device.
297     * Calls to {@link #grantTrust(CharSequence, long, int)} will be ignored until the user has
298     * unlocked the device and {@link #onDeviceUnlocked()} is called.
299     *
300     * @param timeoutMs The amount of time, in milliseconds, that needs to elapse before the user
301     *    can attempt to unlock the device again.
302     */
303    public void onDeviceUnlockLockout(long timeoutMs) {
304    }
305
306    /**
307     * Called when an escrow token is added for user userId.
308     *
309     * @param token the added token
310     * @param handle the handle to the corresponding internal synthetic password. A user is unlocked
311     * by presenting both handle and escrow token.
312     * @param user the user to which the escrow token is added.
313     *
314     */
315    public void onEscrowTokenAdded(byte[] token, long handle, UserHandle user) {
316    }
317
318    /**
319     * Called when an escrow token state is received upon request.
320     *
321     * @param handle the handle to the internal synthetic password.
322     * @param state the state of the requested escrow token, see {@link TokenState}.
323     *
324     */
325    public void onEscrowTokenStateReceived(long handle, @TokenState int tokenState) {
326    }
327
328    /**
329     * Called when an escrow token is removed.
330     *
331     * @param handle the handle to the removed the synthetic password.
332     * @param successful whether the removing operaiton is achieved.
333     *
334     */
335    public void onEscrowTokenRemoved(long handle, boolean successful) {
336    }
337
338    private void onError(String msg) {
339        Slog.v(TAG, "Remote exception while " + msg);
340    }
341
342    /**
343     * Called when device policy admin wants to enable specific options for agent in response to
344     * {@link DevicePolicyManager#setKeyguardDisabledFeatures(ComponentName, int)} and
345     * {@link DevicePolicyManager#setTrustAgentConfiguration(ComponentName, ComponentName,
346     * PersistableBundle)}.
347     * <p>Agents that support configuration options should overload this method and return 'true'.
348     *
349     * @param options The aggregated list of options or an empty list if no restrictions apply.
350     * @return true if it supports configuration options.
351     */
352    public boolean onConfigure(List<PersistableBundle> options) {
353        return false;
354    }
355
356    /**
357     * Call to grant trust on the device.
358     *
359     * @param message describes why the device is trusted, e.g. "Trusted by location".
360     * @param durationMs amount of time in milliseconds to keep the device in a trusted state.
361     *    Trust for this agent will automatically be revoked when the timeout expires unless
362     *    extended by a subsequent call to this function. The timeout is measured from the
363     *    invocation of this function as dictated by {@link SystemClock#elapsedRealtime())}.
364     *    For security reasons, the value should be no larger than necessary.
365     *    The value may be adjusted by the system as necessary to comply with a policy controlled
366     *    by the system or {@link DevicePolicyManager} restrictions. See {@link #onTrustTimeout()}
367     *    for determining when trust expires.
368     * @param initiatedByUser this is a hint to the system that trust is being granted as the
369     *    direct result of user action - such as solving a security challenge. The hint is used
370     *    by the system to optimize the experience. Behavior may vary by device and release, so
371     *    one should only set this parameter if it meets the above criteria rather than relying on
372     *    the behavior of any particular device or release. Corresponds to
373     *    {@link #FLAG_GRANT_TRUST_INITIATED_BY_USER}.
374     * @throws IllegalStateException if the agent is not currently managing trust.
375     *
376     * @deprecated use {@link #grantTrust(CharSequence, long, int)} instead.
377     */
378    @Deprecated
379    public final void grantTrust(
380            final CharSequence message, final long durationMs, final boolean initiatedByUser) {
381        grantTrust(message, durationMs, initiatedByUser ? FLAG_GRANT_TRUST_INITIATED_BY_USER : 0);
382    }
383
384    /**
385     * Call to grant trust on the device.
386     *
387     * @param message describes why the device is trusted, e.g. "Trusted by location".
388     * @param durationMs amount of time in milliseconds to keep the device in a trusted state.
389     *    Trust for this agent will automatically be revoked when the timeout expires unless
390     *    extended by a subsequent call to this function. The timeout is measured from the
391     *    invocation of this function as dictated by {@link SystemClock#elapsedRealtime())}.
392     *    For security reasons, the value should be no larger than necessary.
393     *    The value may be adjusted by the system as necessary to comply with a policy controlled
394     *    by the system or {@link DevicePolicyManager} restrictions. See {@link #onTrustTimeout()}
395     *    for determining when trust expires.
396     * @param flags TBDocumented
397     * @throws IllegalStateException if the agent is not currently managing trust.
398     */
399    public final void grantTrust(
400            final CharSequence message, final long durationMs, @GrantTrustFlags final int flags) {
401        synchronized (mLock) {
402            if (!mManagingTrust) {
403                throw new IllegalStateException("Cannot grant trust if agent is not managing trust."
404                        + " Call setManagingTrust(true) first.");
405            }
406            if (mCallback != null) {
407                try {
408                    mCallback.grantTrust(message.toString(), durationMs, flags);
409                } catch (RemoteException e) {
410                    onError("calling enableTrust()");
411                }
412            } else {
413                // Remember trust has been granted so we can effectively grant it once the service
414                // is bound.
415                mPendingGrantTrustTask = new Runnable() {
416                    @Override
417                    public void run() {
418                        grantTrust(message, durationMs, flags);
419                    }
420                };
421            }
422        }
423    }
424
425    /**
426     * Call to revoke trust on the device.
427     */
428    public final void revokeTrust() {
429        synchronized (mLock) {
430            if (mPendingGrantTrustTask != null) {
431                mPendingGrantTrustTask = null;
432            }
433            if (mCallback != null) {
434                try {
435                    mCallback.revokeTrust();
436                } catch (RemoteException e) {
437                    onError("calling revokeTrust()");
438                }
439            }
440        }
441    }
442
443    /**
444     * Call to notify the system if the agent is ready to manage trust.
445     *
446     * This property is not persistent across recreating the service and defaults to false.
447     * Therefore this method is typically called when initializing the agent in {@link #onCreate}.
448     *
449     * @param managingTrust indicates if the agent would like to manage trust.
450     */
451    public final void setManagingTrust(boolean managingTrust) {
452        synchronized (mLock) {
453            if (mManagingTrust != managingTrust) {
454                mManagingTrust = managingTrust;
455                if (mCallback != null) {
456                    try {
457                        mCallback.setManagingTrust(managingTrust);
458                    } catch (RemoteException e) {
459                        onError("calling setManagingTrust()");
460                    }
461                }
462            }
463        }
464    }
465
466    /**
467     * Call to add an escrow token to derive a synthetic password. A synthetic password is an
468     * alternaive to the user-set password/pin/pattern in order to unlock encrypted disk. An escrow
469     * token can be taken and internally derive the synthetic password. The new added token will not
470     * be acivated until the user input the correct PIN/Passcode/Password once.
471     *
472     * Result will be return by callback {@link #onEscrowTokenAdded(long, int)}
473     *
474     * @param token an escrow token of high entropy.
475     * @param user the user which the escrow token will be added to.
476     *
477     */
478    public final void addEscrowToken(byte[] token, UserHandle user) {
479        synchronized (mLock) {
480            if (mCallback == null) {
481                Slog.w(TAG, "Cannot add escrow token if the agent is not connecting to framework");
482                throw new IllegalStateException("Trust agent is not connected");
483            }
484            try {
485                mCallback.addEscrowToken(token, user.getIdentifier());
486            } catch (RemoteException e) {
487                onError("calling addEscrowToken");
488            }
489        }
490    }
491
492    /**
493     * Call to check the active state of an escrow token.
494     *
495     * Result will be return in callback {@link #onEscrowTokenStateReceived(long, boolean)}
496     *
497     * @param handle the handle of escrow token to the internal synthetic password.
498     * @param user the user which the escrow token is added to.
499     *
500     */
501    public final void isEscrowTokenActive(long handle, UserHandle user) {
502        synchronized (mLock) {
503            if (mCallback == null) {
504                Slog.w(TAG, "Cannot add escrow token if the agent is not connecting to framework");
505                throw new IllegalStateException("Trust agent is not connected");
506            }
507            try {
508                mCallback.isEscrowTokenActive(handle, user.getIdentifier());
509            } catch (RemoteException e) {
510                onError("calling isEscrowTokenActive");
511            }
512        }
513    }
514
515    /**
516     * Call to remove the escrow token.
517     *
518     * Result will be return in callback {@link #onEscrowTokenRemoved(long, boolean)}
519     *
520     * @param handle the handle of escrow tokent to the internal synthetic password.
521     * @param user the user id which the escrow token is added to.
522     *
523     */
524    public final void removeEscrowToken(long handle, UserHandle user) {
525        synchronized (mLock) {
526            if (mCallback == null) {
527                Slog.w(TAG, "Cannot add escrow token if the agent is not connecting to framework");
528                throw new IllegalStateException("Trust agent is not connected");
529            }
530            try {
531                mCallback.removeEscrowToken(handle, user.getIdentifier());
532            } catch (RemoteException e) {
533                onError("callling removeEscrowToken");
534            }
535        }
536    }
537
538    /**
539     * Call to unlock user's FBE.
540     *
541     * @param handle the handle of escrow tokent to the internal synthetic password.
542     * @param token the escrow token
543     * @param user the user about to be unlocked.
544     *
545     */
546    public final void unlockUserWithToken(long handle, byte[] token, UserHandle user) {
547        UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
548        if (um.isUserUnlocked(user)) {
549            Slog.i(TAG, "User already unlocked");
550            return;
551        }
552
553        synchronized (mLock) {
554            if (mCallback == null) {
555                Slog.w(TAG, "Cannot add escrow token if the agent is not connecting to framework");
556                throw new IllegalStateException("Trust agent is not connected");
557            }
558            try {
559                mCallback.unlockUserWithToken(handle, token, user.getIdentifier());
560            } catch (RemoteException e) {
561                onError("calling unlockUserWithToken");
562            }
563        }
564    }
565
566    /**
567     * Request showing a transient error message on the keyguard.
568     * The message will be visible on the lock screen or always on display if possible but can be
569     * overridden by other keyguard events of higher priority - eg. fingerprint auth error.
570     * Other trust agents may override your message if posted simultaneously.
571     *
572     * @param message Message to show.
573     */
574    public final void showKeyguardErrorMessage(@NonNull CharSequence message) {
575        if (message == null) {
576            throw new IllegalArgumentException("message cannot be null");
577        }
578        synchronized (mLock) {
579            if (mCallback == null) {
580                Slog.w(TAG, "Cannot show message because service is not connected to framework.");
581                throw new IllegalStateException("Trust agent is not connected");
582            }
583            try {
584                mCallback.showKeyguardErrorMessage(message);
585            } catch (RemoteException e) {
586                onError("calling showKeyguardErrorMessage");
587            }
588        }
589    }
590
591    @Override
592    public final IBinder onBind(Intent intent) {
593        if (DEBUG) Slog.v(TAG, "onBind() intent = " + intent);
594        return new TrustAgentServiceWrapper();
595    }
596
597    private final class TrustAgentServiceWrapper extends ITrustAgentService.Stub {
598        @Override /* Binder API */
599        public void onUnlockAttempt(boolean successful) {
600            mHandler.obtainMessage(MSG_UNLOCK_ATTEMPT, successful ? 1 : 0, 0).sendToTarget();
601        }
602
603        @Override
604        public void onUnlockLockout(int timeoutMs) {
605            mHandler.obtainMessage(MSG_UNLOCK_LOCKOUT, timeoutMs, 0).sendToTarget();
606        }
607
608        @Override /* Binder API */
609        public void onTrustTimeout() {
610            mHandler.sendEmptyMessage(MSG_TRUST_TIMEOUT);
611        }
612
613        @Override /* Binder API */
614        public void onConfigure(List<PersistableBundle> args, IBinder token) {
615            mHandler.obtainMessage(MSG_CONFIGURE, new ConfigurationData(args, token))
616                    .sendToTarget();
617        }
618
619        @Override
620        public void onDeviceLocked() throws RemoteException {
621            mHandler.obtainMessage(MSG_DEVICE_LOCKED).sendToTarget();
622        }
623
624        @Override
625        public void onDeviceUnlocked() throws RemoteException {
626            mHandler.obtainMessage(MSG_DEVICE_UNLOCKED).sendToTarget();
627        }
628
629        @Override /* Binder API */
630        public void setCallback(ITrustAgentServiceCallback callback) {
631            synchronized (mLock) {
632                mCallback = callback;
633                // The managingTrust property is false implicitly on the server-side, so we only
634                // need to set it here if the agent has decided to manage trust.
635                if (mManagingTrust) {
636                    try {
637                        mCallback.setManagingTrust(mManagingTrust);
638                    } catch (RemoteException e ) {
639                        onError("calling setManagingTrust()");
640                    }
641                }
642                if (mPendingGrantTrustTask != null) {
643                    mPendingGrantTrustTask.run();
644                    mPendingGrantTrustTask = null;
645                }
646            }
647        }
648
649        @Override
650        public void onEscrowTokenAdded(byte[] token, long handle, UserHandle user) {
651            Message msg = mHandler.obtainMessage(MSG_ESCROW_TOKEN_ADDED);
652            msg.getData().putByteArray(EXTRA_TOKEN, token);
653            msg.getData().putLong(EXTRA_TOKEN_HANDLE, handle);
654            msg.getData().putParcelable(EXTRA_USER_HANDLE, user);
655            msg.sendToTarget();
656        }
657
658        public void onTokenStateReceived(long handle, int tokenState) {
659            Message msg = mHandler.obtainMessage(MSG_ESCROW_TOKEN_STATE_RECEIVED);
660            msg.getData().putLong(EXTRA_TOKEN_HANDLE, handle);
661            msg.getData().putInt(EXTRA_TOKEN_STATE, tokenState);
662            msg.sendToTarget();
663        }
664
665        public void onEscrowTokenRemoved(long handle, boolean successful) {
666            Message msg = mHandler.obtainMessage(MSG_ESCROW_TOKEN_REMOVED);
667            msg.getData().putLong(EXTRA_TOKEN_HANDLE, handle);
668            msg.getData().putBoolean(EXTRA_TOKEN_REMOVED_RESULT, successful);
669            msg.sendToTarget();
670        }
671    }
672}
673