NfcService.java revision bf6e5d1655d5ad524a8ec007413c7011ed969df8
1/*
2 * Copyright (C) 2010 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 com.android.nfc;
18
19import com.android.nfc.DeviceHost.DeviceHostListener;
20import com.android.nfc.DeviceHost.LlcpServerSocket;
21import com.android.nfc.DeviceHost.LlcpSocket;
22import com.android.nfc.DeviceHost.NfcDepEndpoint;
23import com.android.nfc.DeviceHost.TagEndpoint;
24import com.android.nfc.nxp.NativeNfcManager;
25import com.android.nfc.nxp.NativeNfcSecureElement;
26import com.android.nfc3.R;
27
28import android.app.Application;
29import android.app.KeyguardManager;
30import android.app.PendingIntent;
31import android.content.BroadcastReceiver;
32import android.content.ComponentName;
33import android.content.ContentResolver;
34import android.content.Context;
35import android.content.Intent;
36import android.content.IntentFilter;
37import android.content.SharedPreferences;
38import android.content.pm.PackageManager;
39import android.media.AudioManager;
40import android.media.SoundPool;
41import android.net.Uri;
42import android.nfc.ErrorCodes;
43import android.nfc.FormatException;
44import android.nfc.INdefPushCallback;
45import android.nfc.INfcAdapter;
46import android.nfc.INfcAdapterExtras;
47import android.nfc.INfcTag;
48import android.nfc.NdefMessage;
49import android.nfc.NfcAdapter;
50import android.nfc.Tag;
51import android.nfc.TechListParcel;
52import android.nfc.TransceiveResult;
53import android.nfc.tech.Ndef;
54import android.nfc.tech.TagTechnology;
55import android.os.AsyncTask;
56import android.os.Bundle;
57import android.os.Handler;
58import android.os.IBinder;
59import android.os.Message;
60import android.os.PowerManager;
61import android.os.Process;
62import android.os.RemoteException;
63import android.os.ServiceManager;
64import android.provider.Settings;
65import android.util.Log;
66
67import java.io.FileDescriptor;
68import java.io.IOException;
69import java.io.PrintWriter;
70import java.util.Arrays;
71import java.util.HashMap;
72import java.util.HashSet;
73import java.util.concurrent.ExecutionException;
74
75public class NfcService extends Application implements DeviceHostListener {
76    private static final String ACTION_MASTER_CLEAR_NOTIFICATION = "android.intent.action.MASTER_CLEAR_NOTIFICATION";
77
78    static final boolean DBG = true;
79    static final String TAG = "NfcService";
80
81    public static final String SERVICE_NAME = "nfc";
82
83    private static final String NFC_PERM = android.Manifest.permission.NFC;
84    private static final String NFC_PERM_ERROR = "NFC permission required";
85    private static final String ADMIN_PERM = android.Manifest.permission.WRITE_SECURE_SETTINGS;
86    private static final String ADMIN_PERM_ERROR = "WRITE_SECURE_SETTINGS permission required";
87    private static final String NFCEE_ADMIN_PERM = "com.android.nfc.permission.NFCEE_ADMIN";
88    private static final String NFCEE_ADMIN_PERM_ERROR = "NFCEE_ADMIN permission required";
89
90    public static final String PREF = "NfcServicePrefs";
91
92    private static final String PREF_NFC_ON = "nfc_on";
93    private static final boolean NFC_ON_DEFAULT = true;
94    private static final String PREF_ZEROCLICK_ON = "zeroclick_on";
95    private static final boolean ZEROCLICK_ON_DEFAULT = true;
96
97    private static final String PREF_FIRST_BOOT = "first_boot";
98
99    static final int MSG_NDEF_TAG = 0;
100    static final int MSG_CARD_EMULATION = 1;
101    static final int MSG_LLCP_LINK_ACTIVATION = 2;
102    static final int MSG_LLCP_LINK_DEACTIVATED = 3;
103    static final int MSG_TARGET_DESELECTED = 4;
104    static final int MSG_MOCK_NDEF = 7;
105    static final int MSG_SE_FIELD_ACTIVATED = 8;
106    static final int MSG_SE_FIELD_DEACTIVATED = 9;
107    static final int MSG_SE_APDU_RECEIVED = 10;
108    static final int MSG_SE_EMV_CARD_REMOVAL = 11;
109    static final int MSG_SE_MIFARE_ACCESS = 12;
110
111    static final int TASK_ENABLE = 1;
112    static final int TASK_DISABLE = 2;
113    static final int TASK_BOOT = 3;
114    static final int TASK_EE_WIPE = 4;
115
116    // Copied from com.android.nfc_extras to avoid library dependency
117    // Must keep in sync with com.android.nfc_extras
118    static final int ROUTE_OFF = 1;
119    static final int ROUTE_ON_WHEN_SCREEN_ON = 2;
120
121    public static final String ACTION_RF_FIELD_ON_DETECTED =
122        "com.android.nfc_extras.action.RF_FIELD_ON_DETECTED";
123    public static final String ACTION_RF_FIELD_OFF_DETECTED =
124        "com.android.nfc_extras.action.RF_FIELD_OFF_DETECTED";
125    public static final String ACTION_AID_SELECTED =
126        "com.android.nfc_extras.action.AID_SELECTED";
127    public static final String EXTRA_AID = "com.android.nfc_extras.extra.AID";
128
129    public static final String ACTION_APDU_RECEIVED =
130        "com.android.nfc_extras.action.APDU_RECEIVED";
131    public static final String EXTRA_APDU_BYTES =
132        "com.android.nfc_extras.extra.APDU_BYTES";
133
134    public static final String ACTION_EMV_CARD_REMOVAL =
135        "com.android.nfc_extras.action.EMV_CARD_REMOVAL";
136
137    public static final String ACTION_MIFARE_ACCESS_DETECTED =
138        "com.android.nfc_extras.action.MIFARE_ACCESS_DETECTED";
139    public static final String EXTRA_MIFARE_BLOCK =
140        "com.android.nfc_extras.extra.MIFARE_BLOCK";
141
142    //TODO: dont hardcode this
143    private static final byte[][] EE_WIPE_APDUS = {
144        {(byte)0x00, (byte)0xa4, (byte)0x04, (byte)0x00, (byte)0x00},
145        {(byte)0x00, (byte)0xa4, (byte)0x04, (byte)0x00, (byte)0x07, (byte)0xa0, (byte)0x00,
146                (byte)0x00, (byte)0x04, (byte)0x76, (byte)0x20, (byte)0x10, (byte)0x00},
147        {(byte)0x80, (byte)0xe2, (byte)0x01, (byte)0x03, (byte)0x00},
148        {(byte)0x00, (byte)0xa4, (byte)0x04, (byte)0x00, (byte)0x00},
149        {(byte)0x00, (byte)0xa4, (byte)0x04, (byte)0x00, (byte)0x07, (byte)0xa0, (byte)0x00,
150                (byte)0x00, (byte)0x04, (byte)0x76, (byte)0x30, (byte)0x30, (byte)0x00},
151        {(byte)0x80, (byte)0xb4, (byte)0x00, (byte)0x00, (byte)0x00},
152        {(byte)0x00, (byte)0xa4, (byte)0x04, (byte)0x00, (byte)0x00},
153    };
154
155    // NFC Execution Environment
156    // fields below are protected by this
157    private NativeNfcSecureElement mSecureElement;
158    private OpenSecureElement mOpenEe;  // null when EE closed
159    private int mEeRoutingState;  // contactless interface routing
160
161    // fields below must be used only on the UI thread and therefore aren't synchronized
162    boolean mP2pStarted = false;
163
164    // fields below are used in multiple threads and protected by synchronized(this)
165    private final HashMap<Integer, Object> mObjectMap = new HashMap<Integer, Object>();
166    private HashSet<String> mSePackages = new HashSet<String>();
167    private boolean mIsScreenUnlocked;
168    private boolean mIsZeroClickRequested;
169
170    // mState is protected by this, however it is only modified in onCreate()
171    // and the default AsyncTask thread so it is read unprotected from that
172    // thread
173    int mState;  // one of NfcAdapter.STATE_ON, STATE_TURNING_ON, etc
174
175    // fields below are final after onCreate()
176    Context mContext;
177    private DeviceHost mDeviceHost;
178    private SharedPreferences mPrefs;
179    private SharedPreferences.Editor mPrefsEditor;
180    private PowerManager.WakeLock mWakeLock;
181    int mStartSound;
182    int mEndSound;
183    int mErrorSound;
184    SoundPool mSoundPool; // playback synchronized on this
185    P2pLinkManager mP2pLinkManager;
186    TagService mNfcTagService;
187    NfcAdapterService mNfcAdapter;
188    NfcAdapterExtrasService mExtrasService;
189    boolean mIsAirplaneSensitive;
190    boolean mIsAirplaneToggleable;
191
192    private NfcDispatcher mNfcDispatcher;
193    private KeyguardManager mKeyguard;
194
195    private static NfcService sService;
196
197    public static void enforceAdminPerm(Context context) {
198        int admin = context.checkCallingOrSelfPermission(ADMIN_PERM);
199        int nfcee = context.checkCallingOrSelfPermission(NFCEE_ADMIN_PERM);
200        if (admin != PackageManager.PERMISSION_GRANTED
201                && nfcee != PackageManager.PERMISSION_GRANTED) {
202            throw new SecurityException(ADMIN_PERM_ERROR);
203        }
204    }
205
206    public static void enforceNfceeAdminPerm(Context context) {
207        context.enforceCallingOrSelfPermission(NFCEE_ADMIN_PERM, NFCEE_ADMIN_PERM_ERROR);
208    }
209
210    public static NfcService getInstance() {
211        return sService;
212    }
213
214    @Override
215    public void onRemoteEndpointDiscovered(TagEndpoint tag) {
216        sendMessage(NfcService.MSG_NDEF_TAG, tag);
217    }
218
219    /**
220     * Notifies transaction
221     */
222    @Override
223    public void onCardEmulationDeselected() {
224        sendMessage(NfcService.MSG_TARGET_DESELECTED, null);
225    }
226
227    /**
228     * Notifies transaction
229     */
230    @Override
231    public void onCardEmulationAidSelected(byte[] aid) {
232        sendMessage(NfcService.MSG_CARD_EMULATION, aid);
233    }
234
235    /**
236     * Notifies P2P Device detected, to activate LLCP link
237     */
238    @Override
239    public void onLlcpLinkActivated(NfcDepEndpoint device) {
240        sendMessage(NfcService.MSG_LLCP_LINK_ACTIVATION, device);
241    }
242
243    /**
244     * Notifies P2P Device detected, to activate LLCP link
245     */
246    @Override
247    public void onLlcpLinkDeactivated(NfcDepEndpoint device) {
248        sendMessage(NfcService.MSG_LLCP_LINK_DEACTIVATED, device);
249    }
250
251    @Override
252    public void onRemoteFieldActivated() {
253        sendMessage(NfcService.MSG_SE_FIELD_ACTIVATED, null);
254    }
255
256    @Override
257    public void onRemoteFieldDeactivated() {
258        sendMessage(NfcService.MSG_SE_FIELD_DEACTIVATED, null);
259    }
260
261    @Override
262    public void onSeApduReceived(byte[] apdu) {
263        sendMessage(NfcService.MSG_SE_APDU_RECEIVED, apdu);
264    }
265
266    @Override
267    public void onSeEmvCardRemoval() {
268        sendMessage(NfcService.MSG_SE_EMV_CARD_REMOVAL, null);
269    }
270
271    @Override
272    public void onSeMifareAccess(byte[] block) {
273        sendMessage(NfcService.MSG_SE_MIFARE_ACCESS, block);
274    }
275
276    @Override
277    public void onCreate() {
278        super.onCreate();
279
280        mNfcTagService = new TagService();
281        mNfcAdapter = new NfcAdapterService();
282        mExtrasService = new NfcAdapterExtrasService();
283
284        Log.i(TAG, "Starting NFC service");
285
286        sService = this;
287
288        mSoundPool = new SoundPool(1, AudioManager.STREAM_NOTIFICATION, 0);
289        mStartSound = mSoundPool.load(this, R.raw.start, 1);
290        mEndSound = mSoundPool.load(this, R.raw.end, 1);
291        mErrorSound = mSoundPool.load(this, R.raw.error, 1);
292
293        mContext = this;
294        mDeviceHost = new NativeNfcManager(this, this);
295
296        mP2pLinkManager = new P2pLinkManager(mContext);
297        mNfcDispatcher = new NfcDispatcher(this, mP2pLinkManager);
298
299        mSecureElement = new NativeNfcSecureElement();
300        mEeRoutingState = ROUTE_OFF;
301
302        mPrefs = getSharedPreferences(PREF, Context.MODE_PRIVATE);
303        mPrefsEditor = mPrefs.edit();
304
305        mState = NfcAdapter.STATE_OFF;
306        mIsZeroClickRequested = mPrefs.getBoolean(PREF_ZEROCLICK_ON, ZEROCLICK_ON_DEFAULT);
307
308        PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
309
310        mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "NfcService");
311        mKeyguard = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
312        mIsScreenUnlocked = pm.isScreenOn() && !mKeyguard.isKeyguardLocked();
313
314        ServiceManager.addService(SERVICE_NAME, mNfcAdapter);
315
316        IntentFilter filter = new IntentFilter(NativeNfcManager.INTERNAL_TARGET_DESELECTED_ACTION);
317        filter.addAction(Intent.ACTION_SCREEN_OFF);
318        filter.addAction(Intent.ACTION_SCREEN_ON);
319        filter.addAction(ACTION_MASTER_CLEAR_NOTIFICATION);
320        filter.addAction(Intent.ACTION_USER_PRESENT);
321        registerForAirplaneMode(filter);
322        registerReceiver(mReceiver, filter);
323
324        filter = new IntentFilter();
325        filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
326        filter.addDataScheme("package");
327
328        registerReceiver(mReceiver, filter);
329
330        new EnableDisableTask().execute(TASK_BOOT);  // do blocking boot tasks
331    }
332
333    void registerForAirplaneMode(IntentFilter filter) {
334        final ContentResolver resolver = mContext.getContentResolver();
335        final String airplaneModeRadios = Settings.System.getString(resolver,
336                Settings.System.AIRPLANE_MODE_RADIOS);
337        final String toggleableRadios = Settings.System.getString(resolver,
338                Settings.System.AIRPLANE_MODE_TOGGLEABLE_RADIOS);
339
340        mIsAirplaneSensitive = airplaneModeRadios == null ? true :
341                airplaneModeRadios.contains(Settings.System.RADIO_NFC);
342        mIsAirplaneToggleable = toggleableRadios == null ? false :
343            toggleableRadios.contains(Settings.System.RADIO_NFC);
344
345        if (mIsAirplaneSensitive) {
346            filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
347        }
348    }
349
350    /**
351     * Manages tasks that involve turning on/off the NFC controller.
352     *
353     * <p>All work that might turn the NFC adapter on or off must be done
354     * through this task, to keep the handling of mState simple.
355     * In other words, mState is only modified in these tasks (and we
356     * don't need a lock to read it in these tasks).
357     *
358     * <p>These tasks are all done on the same AsyncTask background
359     * thread, so they are serialized. Each task may temporarily transition
360     * mState to STATE_TURNING_OFF or STATE_TURNING_ON, but must exit in
361     * either STATE_ON or STATE_OFF. This way each task can be guaranteed
362     * of starting in either STATE_OFF or STATE_ON, without needing to hold
363     * NfcService.this for the entire task.
364     *
365     * <p>AsyncTask's are also implicitly queued. This is useful for corner
366     * cases like turning airplane mode on while TASK_ENABLE is in progress.
367     * The TASK_DISABLE triggered by airplane mode will be correctly executed
368     * immediately after TASK_ENABLE is complete. This seems like the most sane
369     * way to deal with these situations.
370     *
371     * <p>{@link #TASK_ENABLE} enables the NFC adapter, without changing
372     * preferences
373     * <p>{@link #TASK_DISABLE} disables the NFC adapter, without changing
374     * preferences
375     * <p>{@link #TASK_BOOT} does first boot work and may enable NFC
376     * <p>{@link #TASK_EE_WIPE} wipes the Execution Environment, and in the
377     * process may temporarily enable the NFC adapter
378     */
379    class EnableDisableTask extends AsyncTask<Integer, Void, Void> {
380        @Override
381        protected Void doInBackground(Integer... params) {
382            // Sanity check mState
383            switch (mState) {
384                case NfcAdapter.STATE_TURNING_OFF:
385                case NfcAdapter.STATE_TURNING_ON:
386                    Log.e(TAG, "Processing EnableDisable task " + params[0] + " from bad state " +
387                            mState);
388                    return null;
389            }
390
391            /* AsyncTask sets this thread to THREAD_PRIORITY_BACKGROUND,
392             * override with the default. THREAD_PRIORITY_BACKGROUND causes
393             * us to service software I2C too slow for firmware download
394             * with the NXP PN544.
395             * TODO: move this to the DAL I2C layer in libnfc-nxp, since this
396             * problem only occurs on I2C platforms using PN544
397             */
398            Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
399
400            switch (params[0].intValue()) {
401                case TASK_ENABLE:
402                    enableInternal();
403                    break;
404                case TASK_DISABLE:
405                    disableInternal();
406                    break;
407                case TASK_BOOT:
408                    if (mPrefs.getBoolean(PREF_NFC_ON, NFC_ON_DEFAULT) &&
409                            !(mIsAirplaneSensitive && isAirplaneModeOn())) {
410                        enableInternal();
411                    }
412                    if (mPrefs.getBoolean(PREF_FIRST_BOOT, true)) {
413                        Log.i(TAG, "First Boot");
414                        mPrefsEditor.putBoolean(PREF_FIRST_BOOT, false);
415                        mPrefsEditor.apply();
416                        executeEeWipe();
417                    }
418                    break;
419                case TASK_EE_WIPE:
420                    executeEeWipe();
421                    break;
422            }
423
424            // Restore default AsyncTask priority
425            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
426            return null;
427        }
428
429        /**
430         * Enable NFC adapter functions.
431         * Does not toggle preferences.
432         */
433        boolean enableInternal() {
434            if (mState == NfcAdapter.STATE_ON) {
435                return true;
436            }
437            Log.i(TAG, "Enabling NFC");
438            updateState(NfcAdapter.STATE_TURNING_ON);
439
440            if (!mDeviceHost.initialize()) {
441                Log.w(TAG, "Error enabling NFC");
442                updateState(NfcAdapter.STATE_OFF);
443                return false;
444            }
445
446            synchronized(NfcService.this) {
447                mObjectMap.clear();
448
449                mP2pLinkManager.enableDisable(mIsZeroClickRequested, true);
450                updateState(NfcAdapter.STATE_ON);
451            }
452
453            /* Start polling loop */
454            applyRouting();
455            return true;
456        }
457
458        /**
459         * Disable all NFC adapter functions.
460         * Does not toggle preferences.
461         */
462        boolean disableInternal() {
463            if (mState == NfcAdapter.STATE_OFF) {
464                return true;
465            }
466            Log.i(TAG, "Disabling NFC");
467            updateState(NfcAdapter.STATE_TURNING_OFF);
468
469            /* Sometimes mDeviceHost.deinitialize() hangs, use a watch-dog.
470             * Implemented with a new thread (instead of a Handler or AsyncTask),
471             * because the UI Thread and AsyncTask thread-pools can also get hung
472             * when the NFC controller stops responding */
473            WatchDogThread watchDog = new WatchDogThread();
474            watchDog.start();
475
476            mP2pLinkManager.enableDisable(false, false);
477
478            // Stop watchdog if tag present
479            // A convenient way to stop the watchdog properly consists of
480            // disconnecting the tag. The polling loop shall be stopped before
481            // to avoid the tag being discovered again.
482            applyRouting();
483            maybeDisconnectTarget();
484
485            mNfcDispatcher.disableForegroundDispatch();
486
487            boolean result = mDeviceHost.deinitialize();
488            if (DBG) Log.d(TAG, "mDeviceHost.deinitialize() = " + result);
489
490            watchDog.cancel();
491
492            updateState(NfcAdapter.STATE_OFF);
493
494            return result;
495        }
496
497        void executeEeWipe() {
498            // TODO: read SE reset list from /system/etc
499            byte[][]apdus = EE_WIPE_APDUS;
500
501            boolean tempEnable = mState == NfcAdapter.STATE_OFF;
502            if (tempEnable) {
503                if (!enableInternal()) {
504                    Log.w(TAG, "Could not enable NFC to wipe NFC-EE");
505                    return;
506                }
507            }
508            Log.i(TAG, "Executing SE wipe");
509            int handle = mSecureElement.doOpenSecureElementConnection();
510            if (handle == 0) {
511                Log.w(TAG, "Could not open the secure element");
512                if (tempEnable) {
513                    disableInternal();
514                }
515                return;
516            }
517
518            mDeviceHost.setTimeout(TagTechnology.ISO_DEP, 10000);
519
520            for (byte[] cmd : apdus) {
521                byte[] resp = mSecureElement.doTransceive(handle, cmd);
522                if (resp == null) {
523                    Log.w(TAG, "Transceive failed, could not wipe NFC-EE");
524                    break;
525                }
526            }
527
528            mDeviceHost.resetTimeouts();
529            mSecureElement.doDisconnect(handle);
530
531            if (tempEnable) {
532                disableInternal();
533            }
534        }
535
536        void updateState(int newState) {
537            synchronized (this) {
538                if (newState == mState) {
539                    return;
540                }
541                mState = newState;
542                Intent intent = new Intent(NfcAdapter.ACTION_ADAPTER_STATE_CHANGED);
543                intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
544                intent.putExtra(NfcAdapter.EXTRA_ADAPTER_STATE, mState);
545                mContext.sendBroadcast(intent);
546            }
547        }
548    }
549
550    void saveNfcOnSetting(boolean on) {
551        synchronized (NfcService.this) {
552            mPrefsEditor.putBoolean(PREF_NFC_ON, on);
553            mPrefsEditor.apply();
554        }
555    }
556
557    void playSound(int sound) {
558        synchronized (this) {
559            mSoundPool.play(sound, 1.0f, 1.0f, 0, 0, 1.0f);
560        }
561    }
562
563    @Override
564    public void onTerminate() {
565        super.onTerminate();
566        // NFC application is persistent, it should not be destroyed by framework
567        Log.wtf(TAG, "NFC service is under attack!");
568    }
569
570    final class NfcAdapterService extends INfcAdapter.Stub {
571        @Override
572        public boolean enable() throws RemoteException {
573            NfcService.enforceAdminPerm(mContext);
574
575            saveNfcOnSetting(true);
576            if (mIsAirplaneSensitive && isAirplaneModeOn() && !mIsAirplaneToggleable) {
577                Log.i(TAG, "denying enable() request (airplane mode)");
578                return false;
579            }
580            new EnableDisableTask().execute(TASK_ENABLE);
581
582            return true;
583        }
584
585        @Override
586        public boolean disable() throws RemoteException {
587            NfcService.enforceAdminPerm(mContext);
588
589            saveNfcOnSetting(false);
590            new EnableDisableTask().execute(TASK_DISABLE);
591
592            return true;
593        }
594
595        @Override
596        public boolean isZeroClickEnabled() throws RemoteException {
597            synchronized (NfcService.this) {
598                return mIsZeroClickRequested;
599            }
600        }
601
602        @Override
603        public boolean enableZeroClick() throws RemoteException {
604            NfcService.enforceAdminPerm(mContext);
605            synchronized(NfcService.this) {
606                if (mIsZeroClickRequested) {
607                    return true;
608                }
609                Log.i(TAG, "enabling 0-click");
610                mPrefsEditor.putBoolean(PREF_ZEROCLICK_ON, true);
611                mPrefsEditor.apply();
612                mIsZeroClickRequested = true;
613                if (isNfcEnabled()) {
614                    mP2pLinkManager.enableDisable(true, true);
615                }
616            }
617            return true;
618        }
619
620        @Override
621        public boolean disableZeroClick() throws RemoteException {
622            NfcService.enforceAdminPerm(mContext);
623            synchronized(NfcService.this) {
624                if (!mIsZeroClickRequested) {
625                    return true;
626                }
627                Log.i(TAG, "disabling 0-click");
628                mPrefsEditor.putBoolean(PREF_ZEROCLICK_ON, false);
629                mPrefsEditor.apply();
630                mIsZeroClickRequested = false;
631                if (isNfcEnabled()) {
632                    mP2pLinkManager.enableDisable(false, true);
633                }
634            }
635            return true;
636        }
637
638        @Override
639        public void enableForegroundDispatch(ComponentName activity, PendingIntent intent,
640                IntentFilter[] filters, TechListParcel techListsParcel) {
641            // Permission check
642            mContext.enforceCallingOrSelfPermission(NFC_PERM, NFC_PERM_ERROR);
643
644            // Argument validation
645            if (activity == null || intent == null) {
646                throw new IllegalArgumentException();
647            }
648
649            // Validate the IntentFilters
650            if (filters != null) {
651                if (filters.length == 0) {
652                    filters = null;
653                } else {
654                    for (IntentFilter filter : filters) {
655                        if (filter == null) {
656                            throw new IllegalArgumentException("null IntentFilter");
657                        }
658                    }
659                }
660            }
661
662            // Validate the tech lists
663            String[][] techLists = null;
664            if (techListsParcel != null) {
665                techLists = techListsParcel.getTechLists();
666            }
667
668            mNfcDispatcher.enableForegroundDispatch(intent, filters, techLists);
669        }
670
671        @Override
672        public void disableForegroundDispatch(ComponentName activity) {
673            mContext.enforceCallingOrSelfPermission(NFC_PERM, NFC_PERM_ERROR);
674
675            mNfcDispatcher.disableForegroundDispatch();
676        }
677
678        @Override
679        public void enableForegroundNdefPush(ComponentName activity, NdefMessage msg) {
680            mContext.enforceCallingOrSelfPermission(NFC_PERM, NFC_PERM_ERROR);
681            if (activity == null || msg == null) {
682                throw new IllegalArgumentException();
683            }
684            mP2pLinkManager.setNdefToSend(msg, null);
685        }
686
687        @Override
688        public void enableForegroundNdefPushWithCallback(ComponentName activity,
689                INdefPushCallback callback) {
690            mContext.enforceCallingOrSelfPermission(NFC_PERM, NFC_PERM_ERROR);
691            if (activity == null || callback == null) {
692                throw new IllegalArgumentException();
693            }
694            mP2pLinkManager.setNdefToSend(null, callback);
695        }
696
697        @Override
698        public void disableForegroundNdefPush(ComponentName activity) {
699            mContext.enforceCallingOrSelfPermission(NFC_PERM, NFC_PERM_ERROR);
700            mP2pLinkManager.setNdefToSend(null, null);
701        }
702
703        @Override
704        public INfcTag getNfcTagInterface() throws RemoteException {
705            mContext.enforceCallingOrSelfPermission(NFC_PERM, NFC_PERM_ERROR);
706            return mNfcTagService;
707        }
708
709        @Override
710        public INfcAdapterExtras getNfcAdapterExtrasInterface() {
711            NfcService.enforceNfceeAdminPerm(mContext);
712            return mExtrasService;
713        }
714
715        @Override
716        public int getState() throws RemoteException {
717            synchronized (NfcService.this) {
718                return mState;
719            }
720        }
721
722        @Override
723        protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
724            NfcService.this.dump(fd, pw, args);
725        }
726    };
727
728    final class TagService extends INfcTag.Stub {
729        @Override
730        public int close(int nativeHandle) throws RemoteException {
731            mContext.enforceCallingOrSelfPermission(NFC_PERM, NFC_PERM_ERROR);
732
733            TagEndpoint tag = null;
734
735            if (!isNfcEnabled()) {
736                return ErrorCodes.ERROR_NOT_INITIALIZED;
737            }
738
739            /* find the tag in the hmap */
740            tag = (TagEndpoint) findObject(nativeHandle);
741            if (tag != null) {
742                /* Remove the device from the hmap */
743                unregisterObject(nativeHandle);
744                tag.disconnect();
745                return ErrorCodes.SUCCESS;
746            }
747            /* Restart polling loop for notification */
748            applyRouting();
749            return ErrorCodes.ERROR_DISCONNECT;
750        }
751
752        @Override
753        public int connect(int nativeHandle, int technology) throws RemoteException {
754            mContext.enforceCallingOrSelfPermission(NFC_PERM, NFC_PERM_ERROR);
755
756            TagEndpoint tag = null;
757
758            if (!isNfcEnabled()) {
759                return ErrorCodes.ERROR_NOT_INITIALIZED;
760            }
761
762            /* find the tag in the hmap */
763            tag = (TagEndpoint) findObject(nativeHandle);
764            if (tag == null) {
765                return ErrorCodes.ERROR_DISCONNECT;
766            }
767
768            if (technology == TagTechnology.NFC_B) {
769                return ErrorCodes.ERROR_NOT_SUPPORTED;
770            }
771
772            // Note that on most tags, all technologies are behind a single
773            // handle. This means that the connect at the lower levels
774            // will do nothing, as the tag is already connected to that handle.
775            if (tag.connect(technology)) {
776                return ErrorCodes.SUCCESS;
777            } else {
778                return ErrorCodes.ERROR_DISCONNECT;
779            }
780        }
781
782        @Override
783        public int reconnect(int nativeHandle) throws RemoteException {
784            mContext.enforceCallingOrSelfPermission(NFC_PERM, NFC_PERM_ERROR);
785
786            TagEndpoint tag = null;
787
788            // Check if NFC is enabled
789            if (!isNfcEnabled()) {
790                return ErrorCodes.ERROR_NOT_INITIALIZED;
791            }
792
793            /* find the tag in the hmap */
794            tag = (TagEndpoint) findObject(nativeHandle);
795            if (tag != null) {
796                if (tag.reconnect()) {
797                    return ErrorCodes.SUCCESS;
798                } else {
799                    return ErrorCodes.ERROR_DISCONNECT;
800                }
801            }
802            return ErrorCodes.ERROR_DISCONNECT;
803        }
804
805        @Override
806        public int[] getTechList(int nativeHandle) throws RemoteException {
807            mContext.enforceCallingOrSelfPermission(NFC_PERM, NFC_PERM_ERROR);
808
809            // Check if NFC is enabled
810            if (!isNfcEnabled()) {
811                return null;
812            }
813
814            /* find the tag in the hmap */
815            TagEndpoint tag = (TagEndpoint) findObject(nativeHandle);
816            if (tag != null) {
817                return tag.getTechList();
818            }
819            return null;
820        }
821
822        @Override
823        public byte[] getUid(int nativeHandle) throws RemoteException {
824            TagEndpoint tag = null;
825            byte[] uid;
826
827            // Check if NFC is enabled
828            if (!isNfcEnabled()) {
829                return null;
830            }
831
832            /* find the tag in the hmap */
833            tag = (TagEndpoint) findObject(nativeHandle);
834            if (tag != null) {
835                uid = tag.getUid();
836                return uid;
837            }
838            return null;
839        }
840
841        @Override
842        public boolean isPresent(int nativeHandle) throws RemoteException {
843            TagEndpoint tag = null;
844
845            // Check if NFC is enabled
846            if (!isNfcEnabled()) {
847                return false;
848            }
849
850            /* find the tag in the hmap */
851            tag = (TagEndpoint) findObject(nativeHandle);
852            if (tag == null) {
853                return false;
854            }
855
856            return tag.isPresent();
857        }
858
859        @Override
860        public boolean isNdef(int nativeHandle) throws RemoteException {
861            TagEndpoint tag = null;
862
863            // Check if NFC is enabled
864            if (!isNfcEnabled()) {
865                return false;
866            }
867
868            /* find the tag in the hmap */
869            tag = (TagEndpoint) findObject(nativeHandle);
870            int[] ndefInfo = new int[2];
871            if (tag == null) {
872                return false;
873            }
874            return tag.checkNdef(ndefInfo);
875        }
876
877        @Override
878        public TransceiveResult transceive(int nativeHandle, byte[] data, boolean raw)
879                throws RemoteException {
880            mContext.enforceCallingOrSelfPermission(NFC_PERM, NFC_PERM_ERROR);
881
882            TagEndpoint tag = null;
883            byte[] response;
884
885            // Check if NFC is enabled
886            if (!isNfcEnabled()) {
887                return null;
888            }
889
890            /* find the tag in the hmap */
891            tag = (TagEndpoint) findObject(nativeHandle);
892            if (tag != null) {
893                // Check if length is within limits
894                if (data.length > getMaxTransceiveLength(tag.getConnectedTechnology())) {
895                    return new TransceiveResult(TransceiveResult.RESULT_EXCEEDED_LENGTH, null);
896                }
897                int[] targetLost = new int[1];
898                response = tag.transceive(data, raw, targetLost);
899                int result;
900                if (response != null) {
901                    result = TransceiveResult.RESULT_SUCCESS;
902                } else if (targetLost[0] == 1) {
903                    result = TransceiveResult.RESULT_TAGLOST;
904                } else {
905                    result = TransceiveResult.RESULT_FAILURE;
906                }
907                return new TransceiveResult(result, response);
908            }
909            return null;
910        }
911
912        @Override
913        public NdefMessage ndefRead(int nativeHandle) throws RemoteException {
914            mContext.enforceCallingOrSelfPermission(NFC_PERM, NFC_PERM_ERROR);
915
916            TagEndpoint tag;
917
918            // Check if NFC is enabled
919            if (!isNfcEnabled()) {
920                return null;
921            }
922
923            /* find the tag in the hmap */
924            tag = (TagEndpoint) findObject(nativeHandle);
925            if (tag != null) {
926                byte[] buf = tag.readNdef();
927                if (buf == null) {
928                    return null;
929                }
930
931                /* Create an NdefMessage */
932                try {
933                    return new NdefMessage(buf);
934                } catch (FormatException e) {
935                    return null;
936                }
937            }
938            return null;
939        }
940
941        @Override
942        public int ndefWrite(int nativeHandle, NdefMessage msg) throws RemoteException {
943            mContext.enforceCallingOrSelfPermission(NFC_PERM, NFC_PERM_ERROR);
944
945            TagEndpoint tag;
946
947            // Check if NFC is enabled
948            if (!isNfcEnabled()) {
949                return ErrorCodes.ERROR_NOT_INITIALIZED;
950            }
951
952            /* find the tag in the hmap */
953            tag = (TagEndpoint) findObject(nativeHandle);
954            if (tag == null) {
955                return ErrorCodes.ERROR_IO;
956            }
957
958            if (tag.writeNdef(msg.toByteArray())) {
959                return ErrorCodes.SUCCESS;
960            } else {
961                return ErrorCodes.ERROR_IO;
962            }
963
964        }
965
966        @Override
967        public int getLastError(int nativeHandle) throws RemoteException {
968            return(mDeviceHost.doGetLastError());
969        }
970
971        @Override
972        public boolean ndefIsWritable(int nativeHandle) throws RemoteException {
973            throw new UnsupportedOperationException();
974        }
975
976        @Override
977        public int ndefMakeReadOnly(int nativeHandle) throws RemoteException {
978            mContext.enforceCallingOrSelfPermission(NFC_PERM, NFC_PERM_ERROR);
979
980            TagEndpoint tag;
981
982            // Check if NFC is enabled
983            if (!isNfcEnabled()) {
984                return ErrorCodes.ERROR_NOT_INITIALIZED;
985            }
986
987            /* find the tag in the hmap */
988            tag = (TagEndpoint) findObject(nativeHandle);
989            if (tag == null) {
990                return ErrorCodes.ERROR_IO;
991            }
992
993            if (tag.makeReadOnly()) {
994                return ErrorCodes.SUCCESS;
995            } else {
996                return ErrorCodes.ERROR_IO;
997            }
998        }
999
1000        @Override
1001        public int formatNdef(int nativeHandle, byte[] key) throws RemoteException {
1002            mContext.enforceCallingOrSelfPermission(NFC_PERM, NFC_PERM_ERROR);
1003
1004            TagEndpoint tag;
1005
1006            // Check if NFC is enabled
1007            if (!isNfcEnabled()) {
1008                return ErrorCodes.ERROR_NOT_INITIALIZED;
1009            }
1010
1011            /* find the tag in the hmap */
1012            tag = (TagEndpoint) findObject(nativeHandle);
1013            if (tag == null) {
1014                return ErrorCodes.ERROR_IO;
1015            }
1016
1017            if (tag.formatNdef(key)) {
1018                return ErrorCodes.SUCCESS;
1019            } else {
1020                return ErrorCodes.ERROR_IO;
1021            }
1022        }
1023
1024        @Override
1025        public Tag rediscover(int nativeHandle) throws RemoteException {
1026            mContext.enforceCallingOrSelfPermission(NFC_PERM, NFC_PERM_ERROR);
1027
1028            TagEndpoint tag = null;
1029
1030            // Check if NFC is enabled
1031            if (!isNfcEnabled()) {
1032                return null;
1033            }
1034
1035            /* find the tag in the hmap */
1036            tag = (TagEndpoint) findObject(nativeHandle);
1037            if (tag != null) {
1038                // For now the prime usecase for rediscover() is to be able
1039                // to access the NDEF technology after formatting without
1040                // having to remove the tag from the field, or similar
1041                // to have access to NdefFormatable in case low-level commands
1042                // were used to remove NDEF. So instead of doing a full stack
1043                // rediscover (which is poorly supported at the moment anyway),
1044                // we simply remove these two technologies and detect them
1045                // again.
1046                tag.removeTechnology(TagTechnology.NDEF);
1047                tag.removeTechnology(TagTechnology.NDEF_FORMATABLE);
1048                NdefMessage[] msgs = tag.findAndReadNdef();
1049                // Build a new Tag object to return
1050                Tag newTag = new Tag(tag.getUid(), tag.getTechList(),
1051                        tag.getTechExtras(), tag.getHandle(), this);
1052                return newTag;
1053            }
1054            return null;
1055        }
1056
1057        @Override
1058        public int setTimeout(int tech, int timeout) throws RemoteException {
1059            mContext.enforceCallingOrSelfPermission(NFC_PERM, NFC_PERM_ERROR);
1060            boolean success = mDeviceHost.setTimeout(tech, timeout);
1061            if (success) {
1062                return ErrorCodes.SUCCESS;
1063            } else {
1064                return ErrorCodes.ERROR_INVALID_PARAM;
1065            }
1066        }
1067
1068        @Override
1069        public int getTimeout(int tech) throws RemoteException {
1070            mContext.enforceCallingOrSelfPermission(NFC_PERM, NFC_PERM_ERROR);
1071
1072            return mDeviceHost.getTimeout(tech);
1073        }
1074
1075        @Override
1076        public void resetTimeouts() throws RemoteException {
1077            mContext.enforceCallingOrSelfPermission(NFC_PERM, NFC_PERM_ERROR);
1078
1079            mDeviceHost.resetTimeouts();
1080        }
1081
1082        @Override
1083        public boolean canMakeReadOnly(int ndefType) throws RemoteException {
1084            mContext.enforceCallingOrSelfPermission(NFC_PERM, NFC_PERM_ERROR);
1085
1086            return mDeviceHost.canMakeReadOnly(ndefType);
1087        }
1088
1089        @Override
1090        public int getMaxTransceiveLength(int tech) throws RemoteException {
1091            mContext.enforceCallingOrSelfPermission(NFC_PERM, NFC_PERM_ERROR);
1092
1093            return mDeviceHost.getMaxTransceiveLength(tech);
1094        }
1095    };
1096
1097    private void _nfcEeClose(boolean checkPid, int callingPid) throws IOException {
1098        // Blocks until a pending open() or transceive() times out.
1099        //TODO: This is incorrect behavior - the close should interrupt pending
1100        // operations. However this is not supported by current hardware.
1101
1102        synchronized(NfcService.this) {
1103            if (!isNfcEnabled()) {
1104                throw new IOException("NFC adapter is disabled");
1105            }
1106            if (mOpenEe == null) {
1107                throw new IOException("NFC EE closed");
1108            }
1109            if (checkPid && mOpenEe.pid != -1 && callingPid != mOpenEe.pid) {
1110                throw new SecurityException("Wrong PID");
1111            }
1112
1113            mDeviceHost.resetTimeouts();
1114            mSecureElement.doDisconnect(mOpenEe.handle);
1115            mOpenEe = null;
1116
1117            applyRouting();
1118        }
1119    }
1120
1121    final class NfcAdapterExtrasService extends INfcAdapterExtras.Stub {
1122        private Bundle writeNoException() {
1123            Bundle p = new Bundle();
1124            p.putInt("e", 0);
1125            return p;
1126        }
1127        private Bundle writeIoException(IOException e) {
1128            Bundle p = new Bundle();
1129            p.putInt("e", -1);
1130            p.putString("m", e.getMessage());
1131            return p;
1132        }
1133
1134        @Override
1135        public Bundle open(IBinder b) throws RemoteException {
1136            NfcService.enforceNfceeAdminPerm(mContext);
1137
1138            Bundle result;
1139            try {
1140                _open(b);
1141                result = writeNoException();
1142            } catch (IOException e) {
1143                result = writeIoException(e);
1144            }
1145            return result;
1146        }
1147
1148        private void _open(IBinder b) throws IOException, RemoteException {
1149            synchronized(NfcService.this) {
1150                if (!isNfcEnabled()) {
1151                    throw new IOException("NFC adapter is disabled");
1152                }
1153                if (mOpenEe != null) {
1154                    throw new IOException("NFC EE already open");
1155                }
1156
1157                int handle = mSecureElement.doOpenSecureElementConnection();
1158                if (handle == 0) {
1159                    throw new IOException("NFC EE failed to open");
1160                }
1161                mDeviceHost.setTimeout(TagTechnology.ISO_DEP, 10000);
1162
1163                mOpenEe = new OpenSecureElement(getCallingPid(), handle);
1164                try {
1165                    b.linkToDeath(mOpenEe, 0);
1166                } catch (RemoteException e) {
1167                    mOpenEe.binderDied();
1168                }
1169
1170                // Add the calling package to the list of packages that have accessed
1171                // the secure element.
1172                for (String packageName : getPackageManager().getPackagesForUid(getCallingUid())) {
1173                    mSePackages.add(packageName);
1174                }
1175           }
1176        }
1177
1178        @Override
1179        public Bundle close() throws RemoteException {
1180            NfcService.enforceNfceeAdminPerm(mContext);
1181
1182            Bundle result;
1183            try {
1184                _nfcEeClose(true, getCallingPid());
1185                result = writeNoException();
1186            } catch (IOException e) {
1187                result = writeIoException(e);
1188            }
1189            return result;
1190        }
1191
1192        @Override
1193        public Bundle transceive(byte[] in) throws RemoteException {
1194            NfcService.enforceNfceeAdminPerm(mContext);
1195
1196            Bundle result;
1197            byte[] out;
1198            try {
1199                out = _transceive(in);
1200                result = writeNoException();
1201                result.putByteArray("out", out);
1202            } catch (IOException e) {
1203                result = writeIoException(e);
1204            }
1205            return result;
1206        }
1207
1208        private byte[] _transceive(byte[] data) throws IOException, RemoteException {
1209            synchronized(NfcService.this) {
1210                if (!isNfcEnabled()) {
1211                    throw new IOException("NFC is not enabled");
1212                }
1213                if (mOpenEe == null){
1214                    throw new IOException("NFC EE is not open");
1215                }
1216                if (getCallingPid() != mOpenEe.pid) {
1217                    throw new SecurityException("Wrong PID");
1218                }
1219            }
1220
1221            return mSecureElement.doTransceive(mOpenEe.handle, data);
1222        }
1223
1224        @Override
1225        public int getCardEmulationRoute() throws RemoteException {
1226            NfcService.enforceNfceeAdminPerm(mContext);
1227            return mEeRoutingState;
1228        }
1229
1230        @Override
1231        public void setCardEmulationRoute(int route) throws RemoteException {
1232            NfcService.enforceNfceeAdminPerm(mContext);
1233            mEeRoutingState = route;
1234            applyRouting();
1235        }
1236
1237        @Override
1238        public void authenticate(byte[] token) throws RemoteException {
1239            NfcService.enforceNfceeAdminPerm(mContext);
1240        }
1241    };
1242
1243    /** resources kept while secure element is open */
1244    private class OpenSecureElement implements IBinder.DeathRecipient {
1245        public int pid;  // pid that opened SE
1246        public int handle; // low-level handle
1247        public OpenSecureElement(int pid, int handle) {
1248            this.pid = pid;
1249            this.handle = handle;
1250        }
1251        @Override
1252        public void binderDied() {
1253            synchronized (NfcService.this) {
1254                if (DBG) Log.d(TAG, "Tracked app " + pid + " died");
1255                pid = -1;
1256                try {
1257                    _nfcEeClose(false, -1);
1258                } catch (IOException e) { /* already closed */ }
1259            }
1260        }
1261    }
1262
1263    boolean isNfcEnabled() {
1264        synchronized (this) {
1265            return mState == NfcAdapter.STATE_ON;
1266        }
1267    }
1268
1269    class WatchDogThread extends Thread {
1270        boolean mWatchDogCanceled = false;
1271        @Override
1272        public void run() {
1273            boolean slept = false;
1274            while (!slept) {
1275                try {
1276                    Thread.sleep(10000);
1277                    slept = true;
1278                } catch (InterruptedException e) { }
1279            }
1280            synchronized (this) {
1281                if (!mWatchDogCanceled) {
1282                    // Trigger watch-dog
1283                    Log.e(TAG, "Watch dog triggered");
1284                    mDeviceHost.doAbort();
1285                }
1286            }
1287        }
1288        public synchronized void cancel() {
1289            mWatchDogCanceled = true;
1290        }
1291    }
1292
1293    /** apply NFC discovery and EE routing */
1294    private synchronized void applyRouting() {
1295        if (!isNfcEnabled() || mOpenEe != null) {
1296            return;
1297        }
1298        if (mIsScreenUnlocked) {
1299            if (mEeRoutingState == ROUTE_ON_WHEN_SCREEN_ON) {
1300                Log.d(TAG, "NFC-EE routing ON");
1301                mDeviceHost.doSelectSecureElement();
1302            } else {
1303                Log.d(TAG, "NFC-EE routing OFF");
1304                mDeviceHost.doDeselectSecureElement();
1305            }
1306            Log.d(TAG, "NFC-C polling ON");
1307            mDeviceHost.enableDiscovery();
1308        } else {
1309            Log.d(TAG, "NFC-EE routing OFF");
1310            mDeviceHost.doDeselectSecureElement();
1311            Log.d(TAG, "NFC-C polling OFF");
1312            mDeviceHost.disableDiscovery();
1313        }
1314    }
1315
1316    /** Disconnect any target if present */
1317    void maybeDisconnectTarget() {
1318        if (!isNfcEnabled()) {
1319            return;
1320        }
1321        Object[] objectsToDisconnect;
1322        synchronized (this) {
1323            Object[] objectValues = mObjectMap.values().toArray();
1324            // Copy the array before we clear mObjectMap,
1325            // just in case the HashMap values are backed by the same array
1326            objectsToDisconnect = Arrays.copyOf(objectValues, objectValues.length);
1327            mObjectMap.clear();
1328        }
1329        for (Object o : objectsToDisconnect) {
1330            if (DBG) Log.d(TAG, "disconnecting " + o.getClass().getName());
1331            if (o instanceof TagEndpoint) {
1332                // Disconnect from tags
1333                TagEndpoint tag = (TagEndpoint) o;
1334                tag.disconnect();
1335            } else if (o instanceof NfcDepEndpoint) {
1336                // Disconnect from P2P devices
1337                NfcDepEndpoint device = (NfcDepEndpoint) o;
1338                if (device.getMode() == NfcDepEndpoint.MODE_P2P_TARGET) {
1339                    // Remote peer is target, request disconnection
1340                    device.disconnect();
1341                } else {
1342                    // Remote peer is initiator, we cannot disconnect
1343                    // Just wait for field removal
1344                }
1345            }
1346        }
1347    }
1348
1349    Object findObject(int key) {
1350        synchronized (this) {
1351            Object device = mObjectMap.get(key);
1352            if (device == null) {
1353                Log.w(TAG, "Handle not found");
1354            }
1355            return device;
1356        }
1357    }
1358
1359    void registerTagObject(TagEndpoint tag) {
1360        synchronized (this) {
1361            mObjectMap.put(tag.getHandle(), tag);
1362        }
1363    }
1364
1365    void unregisterObject(int handle) {
1366        synchronized (this) {
1367            mObjectMap.remove(handle);
1368        }
1369    }
1370
1371    /** For use by code in this process */
1372    public LlcpSocket createLlcpSocket(int sap, int miu, int rw, int linearBufferLength)
1373            throws IOException, LlcpException {
1374        return mDeviceHost.createLlcpSocket(sap, miu, rw, linearBufferLength);
1375    }
1376
1377    /** For use by code in this process */
1378    public LlcpServerSocket createLlcpServerSocket(int sap, String sn, int miu, int rw,
1379            int linearBufferLength) throws IOException, LlcpException {
1380        return mDeviceHost.createLlcpServerSocket(sap, sn, miu, rw, linearBufferLength);
1381    }
1382
1383    public void sendMockNdefTag(NdefMessage msg) {
1384        sendMessage(MSG_MOCK_NDEF, msg);
1385    }
1386
1387    void sendMessage(int what, Object obj) {
1388        Message msg = mHandler.obtainMessage();
1389        msg.what = what;
1390        msg.obj = obj;
1391        mHandler.sendMessage(msg);
1392    }
1393
1394    final class NfcServiceHandler extends Handler {
1395        @Override
1396        public void handleMessage(Message msg) {
1397            switch (msg.what) {
1398                case MSG_MOCK_NDEF: {
1399                    NdefMessage ndefMsg = (NdefMessage) msg.obj;
1400                    Bundle extras = new Bundle();
1401                    extras.putParcelable(Ndef.EXTRA_NDEF_MSG, ndefMsg);
1402                    extras.putInt(Ndef.EXTRA_NDEF_MAXLENGTH, 0);
1403                    extras.putInt(Ndef.EXTRA_NDEF_CARDSTATE, Ndef.NDEF_MODE_READ_ONLY);
1404                    extras.putInt(Ndef.EXTRA_NDEF_TYPE, Ndef.TYPE_OTHER);
1405                    Tag tag = Tag.createMockTag(new byte[] { 0x00 },
1406                            new int[] { TagTechnology.NDEF },
1407                            new Bundle[] { extras });
1408                    Log.d(TAG, "mock NDEF tag, starting corresponding activity");
1409                    Log.d(TAG, tag.toString());
1410                    boolean delivered = mNfcDispatcher.dispatchTag(tag,
1411                            new NdefMessage[] { ndefMsg });
1412                    if (delivered) {
1413                        playSound(mEndSound);
1414                    } else {
1415                        playSound(mErrorSound);
1416                    }
1417                    break;
1418                }
1419
1420                case MSG_NDEF_TAG:
1421                    if (DBG) Log.d(TAG, "Tag detected, notifying applications");
1422                    TagEndpoint tag = (TagEndpoint) msg.obj;
1423                    playSound(mStartSound);
1424                    NdefMessage[] ndefMsgs = tag.findAndReadNdef();
1425
1426                    if (ndefMsgs != null) {
1427                        tag.startPresenceChecking();
1428                        dispatchTagEndpoint(tag, ndefMsgs);
1429                    } else {
1430                        if (tag.reconnect()) {
1431                            tag.startPresenceChecking();
1432                            dispatchTagEndpoint(tag, null);
1433                        } else {
1434                            tag.disconnect();
1435                            playSound(mErrorSound);
1436                        }
1437                    }
1438                    break;
1439
1440                case MSG_CARD_EMULATION:
1441                    if (DBG) Log.d(TAG, "Card Emulation message");
1442                    byte[] aid = (byte[]) msg.obj;
1443                    /* Send broadcast */
1444                    Intent aidIntent = new Intent();
1445                    aidIntent.setAction(ACTION_AID_SELECTED);
1446                    aidIntent.putExtra(EXTRA_AID, aid);
1447                    if (DBG) Log.d(TAG, "Broadcasting " + ACTION_AID_SELECTED);
1448                    mContext.sendBroadcast(aidIntent, NFCEE_ADMIN_PERM);
1449                    break;
1450
1451                case MSG_SE_EMV_CARD_REMOVAL:
1452                    if (DBG) Log.d(TAG, "Card Removal message");
1453                    /* Send broadcast */
1454                    Intent cardRemovalIntent = new Intent();
1455                    cardRemovalIntent.setAction(ACTION_EMV_CARD_REMOVAL);
1456                    if (DBG) Log.d(TAG, "Broadcasting " + ACTION_EMV_CARD_REMOVAL);
1457                    mContext.sendBroadcast(cardRemovalIntent, NFCEE_ADMIN_PERM);
1458                    break;
1459
1460                case MSG_SE_APDU_RECEIVED:
1461                    if (DBG) Log.d(TAG, "APDU Received message");
1462                    byte[] apduBytes = (byte[]) msg.obj;
1463                    /* Send broadcast */
1464                    Intent apduReceivedIntent = new Intent();
1465                    apduReceivedIntent.setAction(ACTION_APDU_RECEIVED);
1466                    if (apduBytes != null && apduBytes.length > 0) {
1467                        apduReceivedIntent.putExtra(EXTRA_APDU_BYTES, apduBytes);
1468                    }
1469                    if (DBG) Log.d(TAG, "Broadcasting " + ACTION_APDU_RECEIVED);
1470                    mContext.sendBroadcast(apduReceivedIntent, NFCEE_ADMIN_PERM);
1471                    break;
1472
1473                case MSG_SE_MIFARE_ACCESS:
1474                    if (DBG) Log.d(TAG, "MIFARE access message");
1475                    /* Send broadcast */
1476                    byte[] mifareCmd = (byte[]) msg.obj;
1477                    Intent mifareAccessIntent = new Intent();
1478                    mifareAccessIntent.setAction(ACTION_MIFARE_ACCESS_DETECTED);
1479                    if (mifareCmd != null && mifareCmd.length > 1) {
1480                        int mifareBlock = mifareCmd[1] & 0xff;
1481                        if (DBG) Log.d(TAG, "Mifare Block=" + mifareBlock);
1482                        mifareAccessIntent.putExtra(EXTRA_MIFARE_BLOCK, mifareBlock);
1483                    }
1484                    if (DBG) Log.d(TAG, "Broadcasting " + ACTION_MIFARE_ACCESS_DETECTED);
1485                    mContext.sendBroadcast(mifareAccessIntent, NFCEE_ADMIN_PERM);
1486                    break;
1487
1488                case MSG_LLCP_LINK_ACTIVATION:
1489                    llcpActivated((NfcDepEndpoint) msg.obj);
1490                    break;
1491
1492                case MSG_LLCP_LINK_DEACTIVATED:
1493                    NfcDepEndpoint device = (NfcDepEndpoint) msg.obj;
1494                    boolean needsDisconnect = false;
1495
1496                    Log.d(TAG, "LLCP Link Deactivated message. Restart polling loop.");
1497                    synchronized (NfcService.this) {
1498                        /* Check if the device has been already unregistered */
1499                        if (mObjectMap.remove(device.getHandle()) != null) {
1500                            /* Disconnect if we are initiator */
1501                            if (device.getMode() == NfcDepEndpoint.MODE_P2P_TARGET) {
1502                                if (DBG) Log.d(TAG, "disconnecting from target");
1503                                needsDisconnect = true;
1504                            } else {
1505                                if (DBG) Log.d(TAG, "not disconnecting from initiator");
1506                            }
1507                        }
1508                    }
1509                    if (needsDisconnect) {
1510                        device.disconnect();  // restarts polling loop
1511                    }
1512
1513                    mP2pLinkManager.onLlcpDeactivated();
1514                    break;
1515
1516                case MSG_TARGET_DESELECTED:
1517                    /* Broadcast Intent Target Deselected */
1518                    if (DBG) Log.d(TAG, "Target Deselected");
1519                    Intent intent = new Intent();
1520                    intent.setAction(NativeNfcManager.INTERNAL_TARGET_DESELECTED_ACTION);
1521                    if (DBG) Log.d(TAG, "Broadcasting Intent");
1522                    mContext.sendOrderedBroadcast(intent, NFC_PERM);
1523                    break;
1524
1525                case MSG_SE_FIELD_ACTIVATED: {
1526                    if (DBG) Log.d(TAG, "SE FIELD ACTIVATED");
1527                    Intent eventFieldOnIntent = new Intent();
1528                    eventFieldOnIntent.setAction(ACTION_RF_FIELD_ON_DETECTED);
1529                    mContext.sendBroadcast(eventFieldOnIntent, NFCEE_ADMIN_PERM);
1530                    break;
1531                }
1532
1533                case MSG_SE_FIELD_DEACTIVATED: {
1534                    if (DBG) Log.d(TAG, "SE FIELD DEACTIVATED");
1535                    Intent eventFieldOffIntent = new Intent();
1536                    eventFieldOffIntent.setAction(ACTION_RF_FIELD_OFF_DETECTED);
1537                    mContext.sendBroadcast(eventFieldOffIntent, NFCEE_ADMIN_PERM);
1538                    break;
1539                }
1540
1541                default:
1542                    Log.e(TAG, "Unknown message received");
1543                    break;
1544            }
1545        }
1546
1547        private boolean llcpActivated(NfcDepEndpoint device) {
1548            Log.d(TAG, "LLCP Activation message");
1549
1550            if (device.getMode() == NfcDepEndpoint.MODE_P2P_TARGET) {
1551                if (DBG) Log.d(TAG, "NativeP2pDevice.MODE_P2P_TARGET");
1552                if (device.connect()) {
1553                    /* Check LLCP compliancy */
1554                    if (mDeviceHost.doCheckLlcp()) {
1555                        /* Activate LLCP Link */
1556                        if (mDeviceHost.doActivateLlcp()) {
1557                            if (DBG) Log.d(TAG, "Initiator Activate LLCP OK");
1558                            boolean isZeroClickOn;
1559                            synchronized (NfcService.this) {
1560                                // Register P2P device
1561                                mObjectMap.put(device.getHandle(), device);
1562                            }
1563                            mP2pLinkManager.onLlcpActivated();
1564                            return true;
1565                        } else {
1566                            /* should not happen */
1567                            Log.w(TAG, "Initiator LLCP activation failed. Disconnect.");
1568                            device.disconnect();
1569                        }
1570                    } else {
1571                        if (DBG) Log.d(TAG, "Remote Target does not support LLCP. Disconnect.");
1572                        device.disconnect();
1573                    }
1574                } else {
1575                    if (DBG) Log.d(TAG, "Cannot connect remote Target. Polling loop restarted.");
1576                    /*
1577                     * The polling loop should have been restarted in failing
1578                     * doConnect
1579                     */
1580                }
1581            } else if (device.getMode() == NfcDepEndpoint.MODE_P2P_INITIATOR) {
1582                if (DBG) Log.d(TAG, "NativeP2pDevice.MODE_P2P_INITIATOR");
1583                /* Check LLCP compliancy */
1584                if (mDeviceHost.doCheckLlcp()) {
1585                    /* Activate LLCP Link */
1586                    if (mDeviceHost.doActivateLlcp()) {
1587                        if (DBG) Log.d(TAG, "Target Activate LLCP OK");
1588                        boolean isZeroClickOn;
1589                        synchronized (NfcService.this) {
1590                            // Register P2P device
1591                            mObjectMap.put(device.getHandle(), device);
1592                        }
1593                        mP2pLinkManager.onLlcpActivated();
1594                        return true;
1595                    }
1596                } else {
1597                    Log.w(TAG, "checkLlcp failed");
1598                }
1599            }
1600
1601            return false;
1602        }
1603
1604        private void dispatchTagEndpoint(TagEndpoint tagEndpoint, NdefMessage[] msgs) {
1605            Tag tag = new Tag(tagEndpoint.getUid(), tagEndpoint.getTechList(),
1606                    tagEndpoint.getTechExtras(), tagEndpoint.getHandle(), mNfcTagService);
1607            registerTagObject(tagEndpoint);
1608            if (!mNfcDispatcher.dispatchTag(tag, msgs)) {
1609                unregisterObject(tagEndpoint.getHandle());
1610                playSound(mErrorSound);
1611            } else {
1612                playSound(mEndSound);
1613            }
1614        }
1615    }
1616
1617    private NfcServiceHandler mHandler = new NfcServiceHandler();
1618
1619    class EnableDisableDiscoveryTask extends AsyncTask<Boolean, Void, Void> {
1620        @Override
1621        protected Void doInBackground(Boolean... params) {
1622            if (DBG) Log.d(TAG, "EnableDisableDiscoveryTask: enable = " + params[0]);
1623
1624            if (params != null && params.length > 0 && params[0]) {
1625                synchronized (NfcService.this) {
1626                    if (!mIsScreenUnlocked) {
1627                        mIsScreenUnlocked = true;
1628                        applyRouting();
1629                    } else {
1630                        if (DBG) Log.d(TAG, "Ignoring enable request");
1631                    }
1632                }
1633            } else {
1634                mWakeLock.acquire();
1635                synchronized (NfcService.this) {
1636                    if (mIsScreenUnlocked) {
1637                        mIsScreenUnlocked = false;
1638                        applyRouting();
1639                        maybeDisconnectTarget();
1640                    } else {
1641                        if (DBG) Log.d(TAG, "Ignoring disable request");
1642                    }
1643                }
1644                mWakeLock.release();
1645            }
1646            return null;
1647        }
1648    }
1649
1650    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
1651        @Override
1652        public void onReceive(Context context, Intent intent) {
1653            String action = intent.getAction();
1654            if (action.equals(
1655                    NativeNfcManager.INTERNAL_TARGET_DESELECTED_ACTION)) {
1656                if (DBG) Log.d(TAG, "INERNAL_TARGET_DESELECTED_ACTION");
1657
1658                /* Restart polling loop for notification */
1659                applyRouting();
1660
1661            } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
1662                // Only enable if the screen is unlocked. If the screen is locked
1663                // Intent.ACTION_USER_PRESENT will be broadcast when the screen is
1664                // unlocked.
1665                boolean enable = !mKeyguard.isKeyguardSecure() || !mKeyguard.isKeyguardLocked();
1666
1667                // Perform discovery enable in thread to protect against ANR when the
1668                // NFC stack wedges. This is *not* the correct way to fix this issue -
1669                // configuration of the local NFC adapter should be very quick and should
1670                // be safe on the main thread, and the NFC stack should not wedge.
1671                new EnableDisableDiscoveryTask().execute(enable);
1672            } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
1673                // Perform discovery disable in thread to protect against ANR when the
1674                // NFC stack wedges. This is *not* the correct way to fix this issue -
1675                // configuration of the local NFC adapter should be very quick and should
1676                // be safe on the main thread, and the NFC stack should not wedge.
1677                new EnableDisableDiscoveryTask().execute(Boolean.FALSE);
1678            } else if (action.equals(Intent.ACTION_USER_PRESENT)) {
1679                // The user has unlocked the screen. Enabled!
1680                new EnableDisableDiscoveryTask().execute(Boolean.TRUE);
1681            } else if (action.equals(ACTION_MASTER_CLEAR_NOTIFICATION)) {
1682                EnableDisableTask eeWipeTask = new EnableDisableTask();
1683                eeWipeTask.execute(TASK_EE_WIPE);
1684                try {
1685                    eeWipeTask.get();  // blocks until EE wipe is complete
1686                } catch (ExecutionException e) {
1687                    Log.w(TAG, "failed to wipe NFC-EE");
1688                } catch (InterruptedException e) {
1689                    Log.w(TAG, "failed to wipe NFC-EE");
1690                }
1691            } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)) {
1692                boolean dataRemoved = intent.getBooleanExtra(Intent.EXTRA_DATA_REMOVED, false);
1693                if (dataRemoved) {
1694                    Uri data = intent.getData();
1695                    if (data == null) return;
1696                    String packageName = data.getSchemeSpecificPart();
1697
1698                    synchronized (NfcService.this) {
1699                        if (mSePackages.contains(packageName)) {
1700                            new EnableDisableTask().execute(TASK_EE_WIPE);
1701                            mSePackages.remove(packageName);
1702                        }
1703                    }
1704                }
1705            } else if (action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) {
1706                boolean isAirplaneModeOn = intent.getBooleanExtra("state", false);
1707                // Query the airplane mode from Settings.System just to make sure that
1708                // some random app is not sending this intent
1709                if (isAirplaneModeOn != isAirplaneModeOn()) {
1710                    return;
1711                }
1712                if (!mIsAirplaneSensitive) {
1713                    return;
1714                }
1715                if (isAirplaneModeOn) {
1716                    new EnableDisableTask().execute(TASK_DISABLE);
1717                } else if (!isAirplaneModeOn && mPrefs.getBoolean(PREF_NFC_ON, NFC_ON_DEFAULT)) {
1718                    new EnableDisableTask().execute(TASK_ENABLE);
1719                }
1720            }
1721        }
1722    };
1723
1724    /** Returns true if airplane mode is currently on */
1725    boolean isAirplaneModeOn() {
1726        return Settings.System.getInt(mContext.getContentResolver(),
1727                Settings.System.AIRPLANE_MODE_ON, 0) == 1;
1728    }
1729
1730    /** for debugging only - no il8n */
1731    static String stateToString(int state) {
1732        switch (state) {
1733            case NfcAdapter.STATE_OFF:
1734                return "off";
1735            case NfcAdapter.STATE_TURNING_ON:
1736                return "turning on";
1737            case NfcAdapter.STATE_ON:
1738                return "on";
1739            case NfcAdapter.STATE_TURNING_OFF:
1740                return "turning off";
1741            default:
1742                return "<error>";
1743        }
1744    }
1745
1746    void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1747        synchronized (this) {
1748            pw.println("mState=" + stateToString(mState));
1749            pw.println("mIsZeroClickRequested=" + mIsZeroClickRequested);
1750            pw.println("mIsScreenUnlocked=" + mIsScreenUnlocked);
1751            pw.println("mIsAirplaneSensitive=" + mIsAirplaneSensitive);
1752            pw.println("mIsAirplaneToggleable=" + mIsAirplaneToggleable);
1753            mP2pLinkManager.dump(fd, pw, args);
1754        }
1755    }
1756}
1757