1ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito/*
2ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito * Copyright (C) 2015 The Android Open Source Project
3ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito *
4ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito * Licensed under the Apache License, Version 2.0 (the "License");
5ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito * you may not use this file except in compliance with the License.
6ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito * You may obtain a copy of the License at
7ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito *
8ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito *      http://www.apache.org/licenses/LICENSE-2.0
9ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito *
10ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito * Unless required by applicable law or agreed to in writing, software
11ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito * distributed under the License is distributed on an "AS IS" BASIS,
12ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito * See the License for the specific language governing permissions and
14ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito * limitations under the License.
15ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito */
16ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito
17ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Itopackage com.android.nfc.cardemulation;
18ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito
19ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Itoimport android.content.ComponentName;
20ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Itoimport android.content.Context;
21ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Itoimport android.content.Intent;
22ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Itoimport android.content.ServiceConnection;
23ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Itoimport android.nfc.cardemulation.NfcFServiceInfo;
24ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Itoimport android.nfc.cardemulation.HostNfcFService;
25ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Itoimport android.os.Bundle;
26ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Itoimport android.os.Handler;
27ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Itoimport android.os.IBinder;
28ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Itoimport android.os.Message;
29ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Itoimport android.os.Messenger;
30ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Itoimport android.os.RemoteException;
31ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Itoimport android.os.UserHandle;
32ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Itoimport android.util.Log;
33ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito
34ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Itoimport com.android.nfc.NfcService;
35ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito
36ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Itoimport java.io.FileDescriptor;
37ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Itoimport java.io.PrintWriter;
38ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito
39ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Itopublic class HostNfcFEmulationManager {
40ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito    static final String TAG = "HostNfcFEmulationManager";
41ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito    static final boolean DBG = false;
42ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito
43ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito    static final int STATE_IDLE = 0;
44ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito    static final int STATE_W4_SERVICE = 1;
45ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito    static final int STATE_XFER = 2;
46ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito
47ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito    /** NFCID2 length */
48ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito    static final int NFCID2_LENGTH = 8;
49ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito
50ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito    /** Minimum NFC-F packets including length, command code and NFCID2 */
51ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito    static final int MINIMUM_NFCF_PACKET_LENGTH = 10;
52ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito
53ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito    final Context mContext;
54ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito    final RegisteredT3tIdentifiersCache mT3tIdentifiersCache;
55ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito    final Messenger mMessenger = new Messenger (new MessageHandler());
56ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito    final Object mLock;
57ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito
58ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito    // All variables below protected by mLock
59ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito    ComponentName mEnabledFgServiceName;
60ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito
61ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito    Messenger mService;
62ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito    boolean mServiceBound;
63ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito    ComponentName mServiceName;
64ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito
65ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito    // mActiveService denotes the service interface
66ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito    // that is the current active one, until a new packet
67ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito    // comes in that may be resolved to a different service.
68ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito    // On deactivation, mActiveService stops being valid.
69ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito    Messenger mActiveService;
70ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito    ComponentName mActiveServiceName;
71ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito
72ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito    int mState;
73ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito    byte[] mPendingPacket;
74ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito
75ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito    public HostNfcFEmulationManager(Context context,
76ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            RegisteredT3tIdentifiersCache t3tIdentifiersCache) {
77ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        mContext = context;
78ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        mLock = new Object();
79ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        mEnabledFgServiceName = null;
80ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        mT3tIdentifiersCache = t3tIdentifiersCache;
81ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        mState = STATE_IDLE;
82ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito    }
83ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito
84ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito    public void onEnabledForegroundNfcFServiceChanged(ComponentName service) {
85ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        synchronized (mLock) {
86ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            mEnabledFgServiceName = service;
87ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            if (service == null) {
88ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                sendDeactivateToActiveServiceLocked(HostNfcFService.DEACTIVATION_LINK_LOSS);
89ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                unbindServiceIfNeededLocked();
90ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            }
91ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        }
92ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito    }
93ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito
94ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito    public void onHostEmulationActivated() {
95ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        if (DBG) Log.d(TAG, "notifyHostEmulationActivated");
96ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito    }
97ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito
98ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito    public void onHostEmulationData(byte[] data) {
99ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        if (DBG) Log.d(TAG, "notifyHostEmulationData");
100ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        String nfcid2 = findNfcid2(data);
101ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        ComponentName resolvedServiceName = null;
102ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        synchronized (mLock) {
103ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            if (nfcid2 != null) {
104ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                NfcFServiceInfo resolvedService = mT3tIdentifiersCache.resolveNfcid2(nfcid2);
105ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                if (resolvedService != null) {
106ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                    resolvedServiceName = resolvedService.getComponent();
107ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                }
108ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            }
109ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            if (resolvedServiceName == null) {
110ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                if (mActiveServiceName == null) {
111ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                    return;
112ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                }
113ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                resolvedServiceName = mActiveServiceName;
114ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            }
115ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            // Check if resolvedService is actually currently enabled
116ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            if (mEnabledFgServiceName == null ||
117ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                    !mEnabledFgServiceName.equals(resolvedServiceName)) {
118ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                return;
119ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            }
120ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            if (DBG) Log.d(TAG, "resolvedServiceName: " + resolvedServiceName.toString() +
121ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                    "mState: " + String.valueOf(mState));
122ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            switch (mState) {
123ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            case STATE_IDLE:
124ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                Messenger existingService = bindServiceIfNeededLocked(resolvedServiceName);
125ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                if (existingService != null) {
126ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                    Log.d(TAG, "Binding to existing service");
127ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                    mState = STATE_XFER;
128ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                    sendDataToServiceLocked(existingService, data);
129ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                } else {
130ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                    // Waiting for service to be bound
131ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                    Log.d(TAG, "Waiting for new service.");
132ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                    // Queue packet to be used
133ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                    mPendingPacket = data;
134ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                    mState = STATE_W4_SERVICE;
135ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                }
136ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                break;
137ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            case STATE_W4_SERVICE:
138ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                Log.d(TAG, "Unexpected packet in STATE_W4_SERVICE");
139ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                break;
140ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            case STATE_XFER:
141ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                // Regular packet data
142ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                sendDataToServiceLocked(mActiveService, data);
143ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                break;
144ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            }
145ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        }
146ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito    }
147ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito
148ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito    public void onHostEmulationDeactivated() {
149ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        if (DBG) Log.d(TAG, "notifyHostEmulationDeactivated");
150ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        synchronized (mLock) {
151ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            sendDeactivateToActiveServiceLocked(HostNfcFService.DEACTIVATION_LINK_LOSS);
152ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            mActiveService = null;
153ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            mActiveServiceName = null;
154ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            unbindServiceIfNeededLocked();
155ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            mState = STATE_IDLE;
156ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        }
157ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito    }
158ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito
159ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito    public void onNfcDisabled() {
160ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        synchronized (mLock) {
161ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            sendDeactivateToActiveServiceLocked(HostNfcFService.DEACTIVATION_LINK_LOSS);
162ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            mEnabledFgServiceName = null;
163ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            mActiveService = null;
164ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            mActiveServiceName = null;
165ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            unbindServiceIfNeededLocked();
166ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            mState = STATE_IDLE;
167ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        }
168ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito    }
169ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito
170ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito    public void onUserSwitched() {
171ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        synchronized (mLock) {
172ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            sendDeactivateToActiveServiceLocked(HostNfcFService.DEACTIVATION_LINK_LOSS);
173ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            mEnabledFgServiceName = null;
174ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            mActiveService = null;
175ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            mActiveServiceName = null;
176ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            unbindServiceIfNeededLocked();
177ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            mState = STATE_IDLE;
178ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        }
179ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito    }
180ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito
181ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito    void sendDataToServiceLocked(Messenger service, byte[] data) {
182ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        if (DBG) Log.d(TAG, "sendDataToServiceLocked");
183ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        if (DBG) {
184ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            Log.d(TAG, "service: " +
185ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                    (service != null ? service.toString() : "null"));
186ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            Log.d(TAG, "mActiveService: " +
187ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                    (mActiveService != null ? mActiveService.toString() : "null"));
188ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        }
189ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        if (service != mActiveService) {
190ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            sendDeactivateToActiveServiceLocked(HostNfcFService.DEACTIVATION_LINK_LOSS);
191ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            mActiveService = service;
192ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            mActiveServiceName = mServiceName;
193ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        }
194ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        Message msg = Message.obtain(null, HostNfcFService.MSG_COMMAND_PACKET);
195ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        Bundle dataBundle = new Bundle();
196ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        dataBundle.putByteArray("data", data);
197ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        msg.setData(dataBundle);
198ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        msg.replyTo = mMessenger;
199ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        try {
200ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            Log.d(TAG, "Sending data to service");
201ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            if (DBG) Log.d(TAG, "data: " + getByteDump(data));
202ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            mActiveService.send(msg);
203ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        } catch (RemoteException e) {
204ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            Log.e(TAG, "Remote service has died, dropping packet");
205ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        }
206ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito    }
207ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito
208ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito    void sendDeactivateToActiveServiceLocked(int reason) {
209ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        if (DBG) Log.d(TAG, "sendDeactivateToActiveServiceLocked");
210ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        if (mActiveService == null) return;
211ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        Message msg = Message.obtain(null, HostNfcFService.MSG_DEACTIVATED);
212ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        msg.arg1 = reason;
213ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        try {
214ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            mActiveService.send(msg);
215ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        } catch (RemoteException e) {
216ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            // Don't care
217ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        }
218ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito    }
219ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito
220ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito    Messenger bindServiceIfNeededLocked(ComponentName service) {
221ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        if (DBG) Log.d(TAG, "bindServiceIfNeededLocked");
222ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        if (mServiceBound && mServiceName.equals(service)) {
223ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            Log.d(TAG, "Service already bound.");
224ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            return mService;
225ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        } else {
226ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            Log.d(TAG, "Binding to service " + service);
227ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            unbindServiceIfNeededLocked();
228ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            Intent bindIntent = new Intent(HostNfcFService.SERVICE_INTERFACE);
229ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            bindIntent.setComponent(service);
230ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            if (mContext.bindServiceAsUser(bindIntent, mConnection,
231ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                    Context.BIND_AUTO_CREATE, UserHandle.CURRENT)) {
232ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            } else {
233ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                Log.e(TAG, "Could not bind service.");
234ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            }
235ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            return null;
236ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        }
237ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito    }
238ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito
239ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito    void unbindServiceIfNeededLocked() {
240ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        if (DBG) Log.d(TAG, "unbindServiceIfNeededLocked");
241ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        if (mServiceBound) {
242ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            Log.d(TAG, "Unbinding from service " + mServiceName);
243ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            mContext.unbindService(mConnection);
244ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            mServiceBound = false;
245ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            mService = null;
246ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            mServiceName = null;
247ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        }
248ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito    }
249ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito
250ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito    String findNfcid2(byte[] data) {
251ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        if (DBG) Log.d(TAG, "findNfcid2");
252ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        if (data == null || data.length < MINIMUM_NFCF_PACKET_LENGTH) {
253ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            if (DBG) Log.d(TAG, "Data size too small");
254ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            return null;
255ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        }
256ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        int nfcid2Offset = 2;
257ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        return bytesToString(data, nfcid2Offset, NFCID2_LENGTH);
258ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito    }
259ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito
260ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito    private ServiceConnection mConnection = new ServiceConnection() {
261ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        @Override
262ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        public void onServiceConnected(ComponentName name, IBinder service) {
263ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            synchronized (mLock) {
264ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                mService = new Messenger(service);
265ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                mServiceBound = true;
266ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                mServiceName = name;
267ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                Log.d(TAG, "Service bound");
268ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                mState = STATE_XFER;
269ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                // Send pending packet
270ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                if (mPendingPacket != null) {
271ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                    sendDataToServiceLocked(mService, mPendingPacket);
272ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                    mPendingPacket = null;
273ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                }
274ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            }
275ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        }
276ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito
277ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        @Override
278ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        public void onServiceDisconnected(ComponentName name) {
279ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            synchronized (mLock) {
280ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                Log.d(TAG, "Service unbound");
281ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                mService = null;
282ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                mServiceBound = false;
283ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                mServiceName = null;
284ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            }
285ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        }
286ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito    };
287ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito
288ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito    class MessageHandler extends Handler {
289ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        @Override
290ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        public void handleMessage(Message msg) {
291ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            synchronized(mLock) {
292ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                if (mActiveService == null) {
293ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                    Log.d(TAG, "Dropping service response message; service no longer active.");
294ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                    return;
295ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                } else if (!msg.replyTo.getBinder().equals(mActiveService.getBinder())) {
296ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                    Log.d(TAG, "Dropping service response message; service no longer bound.");
297ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                    return;
298ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                }
299ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            }
300ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            if (msg.what == HostNfcFService.MSG_RESPONSE_PACKET) {
301ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                Bundle dataBundle = msg.getData();
302ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                if (dataBundle == null) {
303ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                    return;
304ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                }
305ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                byte[] data = dataBundle.getByteArray("data");
306ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                if (data == null) {
307ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                    return;
308ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                }
309ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                if (data.length == 0) {
310ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                    Log.e(TAG, "Invalid response packet");
311ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                    return;
312ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                }
313ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                if (data.length != (data[0] & 0xff)) {
314ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                    Log.e(TAG, "Invalid response packet");
315ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                    return;
316ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                }
317ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                int state;
318ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                synchronized(mLock) {
319ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                    state = mState;
320ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                }
321ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                if (state == STATE_XFER) {
322ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                    Log.d(TAG, "Sending data");
323ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                    if (DBG) Log.d(TAG, "data:" + getByteDump(data));
324ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                    NfcService.getInstance().sendData(data);
325ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                } else {
326ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                    Log.d(TAG, "Dropping data, wrong state " + Integer.toString(state));
327ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                }
328ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            }
329ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        }
330ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito    }
331ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito
332ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito    static String bytesToString(byte[] bytes, int offset, int length) {
333ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        final char[] hexChars = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
334ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        char[] chars = new char[length * 2];
335ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        int byteValue;
336ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        for (int j = 0; j < length; j++) {
337ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            byteValue = bytes[offset + j] & 0xFF;
338ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            chars[j * 2] = hexChars[byteValue >>> 4];
339ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            chars[j * 2 + 1] = hexChars[byteValue & 0x0F];
340ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        }
341ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        return new String(chars);
342ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito    }
343ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito
344ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito    private String getByteDump(final byte[] cmd) {
345ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        StringBuffer str = new StringBuffer("");
346ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        int letters = 8;
347ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        int i = 0;
348ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito
349ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        if (cmd == null) {
350ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            str.append(" null\n");
351ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            return str.toString();
352ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        }
353ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito
354ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        for (; i < cmd.length; i++) {
355ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            str.append(String.format(" %02X", cmd[i]));
356ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            if ((i % letters == letters - 1) || (i + 1 == cmd.length)) {
357ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito                str.append("\n");
358ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            }
359ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        }
360ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito
361ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        return str.toString();
362ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito    }
363ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito
364ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
365ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        pw.println("Bound services: ");
366ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        if (mServiceBound) {
367ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito            pw.println("    service: " + mServiceName);
368ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito        }
369ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito    }
370ecb8d7bbd1a44cd3edbcb6c5d903fc8d9a625d70Yoshinobu Ito}
371