176a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly/*
276a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly * Copyright (C) 2011 The Android Open Source Project
376a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly *
476a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly * Licensed under the Apache License, Version 2.0 (the "License");
576a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly * you may not use this file except in compliance with the License.
676a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly * You may obtain a copy of the License at
776a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly *
876a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly *      http://www.apache.org/licenses/LICENSE-2.0
976a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly *
1076a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly * Unless required by applicable law or agreed to in writing, software
1176a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly * distributed under the License is distributed on an "AS IS" BASIS,
1276a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1376a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly * See the License for the specific language governing permissions and
1476a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly * limitations under the License.
1576a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly */
1676a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly
1776a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pellypackage com.android.nfc;
1876a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly
19e594363266714de153e5b07f050a05acb2ad6ff9Antony Sargentimport android.Manifest;
2038609ffd78908af4d6bd5156e6b297b357c0dda6Nicolas Prevotimport android.app.ActivityManager;
21b82d80d891077ccd74729e4143925a66eb89eef2Andres Moralesimport android.bluetooth.BluetoothAdapter;
2238609ffd78908af4d6bd5156e6b297b357c0dda6Nicolas Prevotimport android.os.UserManager;
23b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales
24d704c298a5a1e783c71db6f39b2eef0a909b0e88Jeff Hamiltonimport com.android.nfc.RegisteredComponentCache.ComponentInfo;
25b82d80d891077ccd74729e4143925a66eb89eef2Andres Moralesimport com.android.nfc.handover.HandoverDataParser;
26b82d80d891077ccd74729e4143925a66eb89eef2Andres Moralesimport com.android.nfc.handover.PeripheralHandoverService;
27d704c298a5a1e783c71db6f39b2eef0a909b0e88Jeff Hamilton
2876a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pellyimport android.app.Activity;
29525c260303268a83da4c3413b953d13c9084e834The Android Open Source Projectimport android.app.ActivityManager;
30e594363266714de153e5b07f050a05acb2ad6ff9Antony Sargentimport android.app.AlertDialog;
3176a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pellyimport android.app.IActivityManager;
3276a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pellyimport android.app.PendingIntent;
3376a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pellyimport android.app.PendingIntent.CanceledException;
3476a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pellyimport android.content.ComponentName;
35391cfe2479eca2080c14d1832599ad51cafae918Nick Pellyimport android.content.ContentResolver;
3676a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pellyimport android.content.Context;
37e594363266714de153e5b07f050a05acb2ad6ff9Antony Sargentimport android.content.DialogInterface;
3876a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pellyimport android.content.Intent;
3976a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pellyimport android.content.IntentFilter;
4076a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pellyimport android.content.pm.PackageManager;
41525c260303268a83da4c3413b953d13c9084e834The Android Open Source Projectimport android.content.pm.PackageManager.NameNotFoundException;
4276a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pellyimport android.content.pm.ResolveInfo;
437d8987f233985a5ff29226890e11012275d325f5Martijn Coenenimport android.content.res.Resources.NotFoundException;
4476a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pellyimport android.net.Uri;
4576a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pellyimport android.nfc.NdefMessage;
4676a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pellyimport android.nfc.NdefRecord;
4776a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pellyimport android.nfc.NfcAdapter;
4876a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pellyimport android.nfc.Tag;
49ea78e5beebff19ece23870b4ff5e5fd69d61aaa1Nick Pellyimport android.nfc.tech.Ndef;
50492469777a9bf75c5f9232256746acdfca3d4cdeBartek Teodorczykimport android.nfc.tech.NfcBarcode;
5176a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pellyimport android.os.RemoteException;
52525c260303268a83da4c3413b953d13c9084e834The Android Open Source Projectimport android.os.UserHandle;
5376a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pellyimport android.util.Log;
54e594363266714de153e5b07f050a05acb2ad6ff9Antony Sargentimport android.view.LayoutInflater;
55e594363266714de153e5b07f050a05acb2ad6ff9Antony Sargentimport android.view.View;
56e594363266714de153e5b07f050a05acb2ad6ff9Antony Sargentimport android.view.WindowManager;
57e594363266714de153e5b07f050a05acb2ad6ff9Antony Sargentimport android.widget.TextView;
5876a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly
59391cfe2479eca2080c14d1832599ad51cafae918Nick Pellyimport java.io.FileDescriptor;
60391cfe2479eca2080c14d1832599ad51cafae918Nick Pellyimport java.io.PrintWriter;
61893bc90a71883f8d2bfc82818abbf21c9745efdaElliott Hughesimport java.nio.charset.StandardCharsets;
6276a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pellyimport java.util.ArrayList;
6376a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pellyimport java.util.Arrays;
64391cfe2479eca2080c14d1832599ad51cafae918Nick Pellyimport java.util.LinkedList;
65ea9898eb8041802bca345b59de9e0e9302e8232bBen Dodsonimport java.util.List;
66e594363266714de153e5b07f050a05acb2ad6ff9Antony Sargentimport java.util.Locale;
6776a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly
6876a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly/**
6976a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly * Dispatch of NFC events to start activities
7076a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly */
714309f9c7c0097bdd95cf791eef78700b106dec21Andres Moralesclass NfcDispatcher {
7232cdfbf250f2611bb7624c13cfd7d111a847676eAndres Morales    private static final boolean DBG = false;
7332cdfbf250f2611bb7624c13cfd7d111a847676eAndres Morales    private static final String TAG = "NfcDispatcher";
7432cdfbf250f2611bb7624c13cfd7d111a847676eAndres Morales
7532cdfbf250f2611bb7624c13cfd7d111a847676eAndres Morales    static final int DISPATCH_SUCCESS = 1;
7632cdfbf250f2611bb7624c13cfd7d111a847676eAndres Morales    static final int DISPATCH_FAIL = 2;
7732cdfbf250f2611bb7624c13cfd7d111a847676eAndres Morales    static final int DISPATCH_UNLOCK = 3;
7832cdfbf250f2611bb7624c13cfd7d111a847676eAndres Morales
7932cdfbf250f2611bb7624c13cfd7d111a847676eAndres Morales    private final Context mContext;
8032cdfbf250f2611bb7624c13cfd7d111a847676eAndres Morales    private final IActivityManager mIActivityManager;
8132cdfbf250f2611bb7624c13cfd7d111a847676eAndres Morales    private final RegisteredComponentCache mTechListFilters;
8232cdfbf250f2611bb7624c13cfd7d111a847676eAndres Morales    private final ContentResolver mContentResolver;
83b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales    private final HandoverDataParser mHandoverDataParser;
8432cdfbf250f2611bb7624c13cfd7d111a847676eAndres Morales    private final String[] mProvisioningMimes;
850f4ebd13084ff9b9c9b3fcdc4ec8ab6509f9995fRuchi Kandoi    private final String[] mLiveCaseMimes;
8632cdfbf250f2611bb7624c13cfd7d111a847676eAndres Morales    private final ScreenStateHelper mScreenStateHelper;
8716ee2fe0e0f6d2f811bd06c37cbd53c8b395e6d6Andres Morales    private final NfcUnlockManager mNfcUnlockManager;
88b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales    private final boolean mDeviceSupportsBluetooth;
89222b8c18c4a9cf800f2dd615ef116f796ef7942cMartijn Coenen
9076a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly    // Locked on this
9132cdfbf250f2611bb7624c13cfd7d111a847676eAndres Morales    private PendingIntent mOverrideIntent;
9232cdfbf250f2611bb7624c13cfd7d111a847676eAndres Morales    private IntentFilter[] mOverrideFilters;
9332cdfbf250f2611bb7624c13cfd7d111a847676eAndres Morales    private String[][] mOverrideTechLists;
9432cdfbf250f2611bb7624c13cfd7d111a847676eAndres Morales    private boolean mProvisioningOnly;
9576a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly
964309f9c7c0097bdd95cf791eef78700b106dec21Andres Morales    NfcDispatcher(Context context,
97b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales                  HandoverDataParser handoverDataParser,
9844a27fea4133fd8fa0afaad8db4480afe005140dHiroki Yamamoto                  boolean provisionOnly,
9944a27fea4133fd8fa0afaad8db4480afe005140dHiroki Yamamoto                  boolean isLiveCaseEnabled) {
10076a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly        mContext = context;
1013da6676ebb7a18a53621a5bc7eec21ca24becdf0Sudheer Shanka        mIActivityManager = ActivityManager.getService();
10276a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly        mTechListFilters = new RegisteredComponentCache(mContext,
10376a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly                NfcAdapter.ACTION_TECH_DISCOVERED, NfcAdapter.ACTION_TECH_DISCOVERED);
104391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        mContentResolver = context.getContentResolver();
105b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales        mHandoverDataParser = handoverDataParser;
1064309f9c7c0097bdd95cf791eef78700b106dec21Andres Morales        mScreenStateHelper = new ScreenStateHelper(context);
10716ee2fe0e0f6d2f811bd06c37cbd53c8b395e6d6Andres Morales        mNfcUnlockManager = NfcUnlockManager.getInstance();
108b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales        mDeviceSupportsBluetooth = BluetoothAdapter.getDefaultAdapter() != null;
1094309f9c7c0097bdd95cf791eef78700b106dec21Andres Morales
1107d8987f233985a5ff29226890e11012275d325f5Martijn Coenen        synchronized (this) {
1117d8987f233985a5ff29226890e11012275d325f5Martijn Coenen            mProvisioningOnly = provisionOnly;
1127d8987f233985a5ff29226890e11012275d325f5Martijn Coenen        }
1137d8987f233985a5ff29226890e11012275d325f5Martijn Coenen        String[] provisionMimes = null;
1147d8987f233985a5ff29226890e11012275d325f5Martijn Coenen        if (provisionOnly) {
1157d8987f233985a5ff29226890e11012275d325f5Martijn Coenen            try {
1167d8987f233985a5ff29226890e11012275d325f5Martijn Coenen                // Get accepted mime-types
1177d8987f233985a5ff29226890e11012275d325f5Martijn Coenen                provisionMimes = context.getResources().
1187d8987f233985a5ff29226890e11012275d325f5Martijn Coenen                        getStringArray(R.array.provisioning_mime_types);
1197d8987f233985a5ff29226890e11012275d325f5Martijn Coenen            } catch (NotFoundException e) {
1207d8987f233985a5ff29226890e11012275d325f5Martijn Coenen               provisionMimes = null;
1217d8987f233985a5ff29226890e11012275d325f5Martijn Coenen            }
1227d8987f233985a5ff29226890e11012275d325f5Martijn Coenen        }
1237d8987f233985a5ff29226890e11012275d325f5Martijn Coenen        mProvisioningMimes = provisionMimes;
1240f4ebd13084ff9b9c9b3fcdc4ec8ab6509f9995fRuchi Kandoi
1250f4ebd13084ff9b9c9b3fcdc4ec8ab6509f9995fRuchi Kandoi        String[] liveCaseMimes = null;
12644a27fea4133fd8fa0afaad8db4480afe005140dHiroki Yamamoto        if (isLiveCaseEnabled) {
12744a27fea4133fd8fa0afaad8db4480afe005140dHiroki Yamamoto            try {
12844a27fea4133fd8fa0afaad8db4480afe005140dHiroki Yamamoto                // Get accepted mime-types
12944a27fea4133fd8fa0afaad8db4480afe005140dHiroki Yamamoto                liveCaseMimes = context.getResources().
13044a27fea4133fd8fa0afaad8db4480afe005140dHiroki Yamamoto                        getStringArray(R.array.live_case_mime_types);
13144a27fea4133fd8fa0afaad8db4480afe005140dHiroki Yamamoto            } catch (NotFoundException e) {
13244a27fea4133fd8fa0afaad8db4480afe005140dHiroki Yamamoto               liveCaseMimes = null;
13344a27fea4133fd8fa0afaad8db4480afe005140dHiroki Yamamoto            }
1340f4ebd13084ff9b9c9b3fcdc4ec8ab6509f9995fRuchi Kandoi        }
1350f4ebd13084ff9b9c9b3fcdc4ec8ab6509f9995fRuchi Kandoi        mLiveCaseMimes = liveCaseMimes;
13676a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly    }
13776a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly
1380b3b8ab69835cb66c96691dae3ba9b75705980a5Nick Pelly    public synchronized void setForegroundDispatch(PendingIntent intent,
13976a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly            IntentFilter[] filters, String[][] techLists) {
1400b3b8ab69835cb66c96691dae3ba9b75705980a5Nick Pelly        if (DBG) Log.d(TAG, "Set Foreground Dispatch");
14176a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly        mOverrideIntent = intent;
14276a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly        mOverrideFilters = filters;
14376a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly        mOverrideTechLists = techLists;
14476a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly    }
14576a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly
1467d8987f233985a5ff29226890e11012275d325f5Martijn Coenen    public synchronized void disableProvisioningMode() {
1477d8987f233985a5ff29226890e11012275d325f5Martijn Coenen       mProvisioningOnly = false;
1487d8987f233985a5ff29226890e11012275d325f5Martijn Coenen    }
1497d8987f233985a5ff29226890e11012275d325f5Martijn Coenen
150391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly    /**
151391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly     * Helper for re-used objects and methods during a single tag dispatch.
152391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly     */
153391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly    static class DispatchInfo {
154391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        public final Intent intent;
155391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly
156391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        final Intent rootIntent;
157391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        final Uri ndefUri;
158391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        final String ndefMimeType;
159391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        final PackageManager packageManager;
160391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        final Context context;
161391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly
162391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        public DispatchInfo(Context context, Tag tag, NdefMessage message) {
163391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            intent = new Intent();
164391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            intent.putExtra(NfcAdapter.EXTRA_TAG, tag);
165391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            intent.putExtra(NfcAdapter.EXTRA_ID, tag.getId());
166391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            if (message != null) {
167391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly                intent.putExtra(NfcAdapter.EXTRA_NDEF_MESSAGES, new NdefMessage[] {message});
168391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly                ndefUri = message.getRecords()[0].toUri();
169391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly                ndefMimeType = message.getRecords()[0].toMimeType();
170391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            } else {
171391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly                ndefUri = null;
172391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly                ndefMimeType = null;
173391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            }
174391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly
175391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            rootIntent = new Intent(context, NfcRootActivity.class);
176391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            rootIntent.putExtra(NfcRootActivity.EXTRA_LAUNCH_INTENT, intent);
177391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            rootIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
178391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly
179391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            this.context = context;
180391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            packageManager = context.getPackageManager();
18176a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly        }
18276a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly
183391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        public Intent setNdefIntent() {
184391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            intent.setAction(NfcAdapter.ACTION_NDEF_DISCOVERED);
185391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            if (ndefUri != null) {
186391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly                intent.setData(ndefUri);
187391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly                return intent;
188391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            } else if (ndefMimeType != null) {
189391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly                intent.setType(ndefMimeType);
190391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly                return intent;
191391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            }
192391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            return null;
193391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        }
194391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly
195391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        public Intent setTechIntent() {
196391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            intent.setData(null);
197391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            intent.setType(null);
198391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            intent.setAction(NfcAdapter.ACTION_TECH_DISCOVERED);
199391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            return intent;
200391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        }
201391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly
202391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        public Intent setTagIntent() {
203391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            intent.setData(null);
204391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            intent.setType(null);
205391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            intent.setAction(NfcAdapter.ACTION_TAG_DISCOVERED);
206391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            return intent;
207391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        }
208391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly
209e594363266714de153e5b07f050a05acb2ad6ff9Antony Sargent        public boolean isWebIntent() {
210e594363266714de153e5b07f050a05acb2ad6ff9Antony Sargent            return ndefUri != null && ndefUri.normalizeScheme().getScheme().startsWith("http");
211e594363266714de153e5b07f050a05acb2ad6ff9Antony Sargent        }
212e594363266714de153e5b07f050a05acb2ad6ff9Antony Sargent
213e594363266714de153e5b07f050a05acb2ad6ff9Antony Sargent        public String getUri() {
214e594363266714de153e5b07f050a05acb2ad6ff9Antony Sargent            return ndefUri.toString();
215e594363266714de153e5b07f050a05acb2ad6ff9Antony Sargent        }
216e594363266714de153e5b07f050a05acb2ad6ff9Antony Sargent
217391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        /**
218391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly         * Launch the activity via a (single) NFC root task, so that it
219391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly         * creates a new task stack instead of interfering with any existing
220391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly         * task stack for that activity.
221391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly         * NfcRootActivity acts as the task root, it immediately calls
222391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly         * start activity on the intent it is passed.
223391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly         */
224391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        boolean tryStartActivity() {
225391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            // Ideally we'd have used startActivityForResult() to determine whether the
226391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            // NfcRootActivity was able to launch the intent, but startActivityForResult()
227391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            // is not available on Context. Instead, we query the PackageManager beforehand
228391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            // to determine if there is an Activity to handle this intent, and base the
229391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            // result of off that.
230525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project            List<ResolveInfo> activities = packageManager.queryIntentActivitiesAsUser(intent, 0,
231525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project                    ActivityManager.getCurrentUser());
232391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            if (activities.size() > 0) {
233525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project                context.startActivityAsUser(rootIntent, UserHandle.CURRENT);
234391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly                return true;
235391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            }
236391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            return false;
237391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        }
238391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly
239391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        boolean tryStartActivity(Intent intentToStart) {
240525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project            List<ResolveInfo> activities = packageManager.queryIntentActivitiesAsUser(
241525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project                    intentToStart, 0, ActivityManager.getCurrentUser());
242391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            if (activities.size() > 0) {
243391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly                rootIntent.putExtra(NfcRootActivity.EXTRA_LAUNCH_INTENT, intentToStart);
244525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project                context.startActivityAsUser(rootIntent, UserHandle.CURRENT);
245391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly                return true;
246391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            }
247391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            return false;
248391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        }
249391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly    }
250391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly
25132cdfbf250f2611bb7624c13cfd7d111a847676eAndres Morales    /** Returns:
25232cdfbf250f2611bb7624c13cfd7d111a847676eAndres Morales     * <ul>
25332cdfbf250f2611bb7624c13cfd7d111a847676eAndres Morales     *  <li /> DISPATCH_SUCCESS if dispatched to an activity,
25432cdfbf250f2611bb7624c13cfd7d111a847676eAndres Morales     *  <li /> DISPATCH_FAIL if no activities were found to dispatch to,
25532cdfbf250f2611bb7624c13cfd7d111a847676eAndres Morales     *  <li /> DISPATCH_UNLOCK if the tag was used to unlock the device
25632cdfbf250f2611bb7624c13cfd7d111a847676eAndres Morales     * </ul>
25732cdfbf250f2611bb7624c13cfd7d111a847676eAndres Morales     */
25832cdfbf250f2611bb7624c13cfd7d111a847676eAndres Morales    public int dispatchTag(Tag tag) {
25976a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly        PendingIntent overrideIntent;
260391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        IntentFilter[] overrideFilters;
26176a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly        String[][] overrideTechLists;
26293aa66518ba76514149a749937529095ef8b5761Martijn Coenen        String[] provisioningMimes;
2630f4ebd13084ff9b9c9b3fcdc4ec8ab6509f9995fRuchi Kandoi        String[] liveCaseMimes;
2640f4ebd13084ff9b9c9b3fcdc4ec8ab6509f9995fRuchi Kandoi        NdefMessage message = null;
2657d8987f233985a5ff29226890e11012275d325f5Martijn Coenen        boolean provisioningOnly;
266391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly
26776a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly        synchronized (this) {
26876a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly            overrideFilters = mOverrideFilters;
26976a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly            overrideIntent = mOverrideIntent;
27076a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly            overrideTechLists = mOverrideTechLists;
2717d8987f233985a5ff29226890e11012275d325f5Martijn Coenen            provisioningOnly = mProvisioningOnly;
27293aa66518ba76514149a749937529095ef8b5761Martijn Coenen            provisioningMimes = mProvisioningMimes;
2730f4ebd13084ff9b9c9b3fcdc4ec8ab6509f9995fRuchi Kandoi            liveCaseMimes = mLiveCaseMimes;
27476a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly        }
27576a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly
2766ac9196b397a5f3554fe1c416bc005bcef9ac520Andres Morales        boolean screenUnlocked = false;
2770f4ebd13084ff9b9c9b3fcdc4ec8ab6509f9995fRuchi Kandoi        boolean liveCaseDetected = false;
2780f4ebd13084ff9b9c9b3fcdc4ec8ab6509f9995fRuchi Kandoi        Ndef ndef = Ndef.get(tag);
27928a49f0de26c4cc6f0b8c30dc67bebcb1a3110b6Andres Morales        if (!provisioningOnly &&
28028a49f0de26c4cc6f0b8c30dc67bebcb1a3110b6Andres Morales                mScreenStateHelper.checkScreenState() == ScreenStateHelper.SCREEN_STATE_ON_LOCKED) {
281731fd8a52cec0dbe8d6ec6a4c7aa36ff52812112Andres Morales            screenUnlocked = handleNfcUnlock(tag);
2820f4ebd13084ff9b9c9b3fcdc4ec8ab6509f9995fRuchi Kandoi
2830f4ebd13084ff9b9c9b3fcdc4ec8ab6509f9995fRuchi Kandoi            if (ndef != null) {
2840f4ebd13084ff9b9c9b3fcdc4ec8ab6509f9995fRuchi Kandoi                message = ndef.getCachedNdefMessage();
2850f4ebd13084ff9b9c9b3fcdc4ec8ab6509f9995fRuchi Kandoi                if (message != null) {
2860f4ebd13084ff9b9c9b3fcdc4ec8ab6509f9995fRuchi Kandoi                    String ndefMimeType = message.getRecords()[0].toMimeType();
2870f4ebd13084ff9b9c9b3fcdc4ec8ab6509f9995fRuchi Kandoi                    if (liveCaseMimes != null &&
2880f4ebd13084ff9b9c9b3fcdc4ec8ab6509f9995fRuchi Kandoi                            Arrays.asList(liveCaseMimes).contains(ndefMimeType)) {
2890f4ebd13084ff9b9c9b3fcdc4ec8ab6509f9995fRuchi Kandoi                        liveCaseDetected = true;
2900f4ebd13084ff9b9c9b3fcdc4ec8ab6509f9995fRuchi Kandoi                    }
2910f4ebd13084ff9b9c9b3fcdc4ec8ab6509f9995fRuchi Kandoi                }
29228a49f0de26c4cc6f0b8c30dc67bebcb1a3110b6Andres Morales            }
2930f4ebd13084ff9b9c9b3fcdc4ec8ab6509f9995fRuchi Kandoi
2940f4ebd13084ff9b9c9b3fcdc4ec8ab6509f9995fRuchi Kandoi            if (!screenUnlocked && !liveCaseDetected)
2950f4ebd13084ff9b9c9b3fcdc4ec8ab6509f9995fRuchi Kandoi                return DISPATCH_FAIL;
29628a49f0de26c4cc6f0b8c30dc67bebcb1a3110b6Andres Morales        }
29728a49f0de26c4cc6f0b8c30dc67bebcb1a3110b6Andres Morales
2984309f9c7c0097bdd95cf791eef78700b106dec21Andres Morales        if (ndef != null) {
2994309f9c7c0097bdd95cf791eef78700b106dec21Andres Morales            message = ndef.getCachedNdefMessage();
300492469777a9bf75c5f9232256746acdfca3d4cdeBartek Teodorczyk        } else {
301492469777a9bf75c5f9232256746acdfca3d4cdeBartek Teodorczyk            NfcBarcode nfcBarcode = NfcBarcode.get(tag);
302492469777a9bf75c5f9232256746acdfca3d4cdeBartek Teodorczyk            if (nfcBarcode != null && nfcBarcode.getType() == NfcBarcode.TYPE_KOVIO) {
303492469777a9bf75c5f9232256746acdfca3d4cdeBartek Teodorczyk                message = decodeNfcBarcodeUri(nfcBarcode);
304492469777a9bf75c5f9232256746acdfca3d4cdeBartek Teodorczyk            }
3054309f9c7c0097bdd95cf791eef78700b106dec21Andres Morales        }
3064309f9c7c0097bdd95cf791eef78700b106dec21Andres Morales
3075ef11b7525885a42e681c31ebe2adb8b9317d0d2Martijn Coenen        if (DBG) Log.d(TAG, "dispatch tag: " + tag.toString() + " message: " + message);
3085ef11b7525885a42e681c31ebe2adb8b9317d0d2Martijn Coenen
3095ef11b7525885a42e681c31ebe2adb8b9317d0d2Martijn Coenen        DispatchInfo dispatch = new DispatchInfo(mContext, tag, message);
3105ef11b7525885a42e681c31ebe2adb8b9317d0d2Martijn Coenen
3115ef11b7525885a42e681c31ebe2adb8b9317d0d2Martijn Coenen        resumeAppSwitches();
3125ef11b7525885a42e681c31ebe2adb8b9317d0d2Martijn Coenen
3135ef11b7525885a42e681c31ebe2adb8b9317d0d2Martijn Coenen        if (tryOverrides(dispatch, tag, message, overrideIntent, overrideFilters,
3145ef11b7525885a42e681c31ebe2adb8b9317d0d2Martijn Coenen                overrideTechLists)) {
3155ef11b7525885a42e681c31ebe2adb8b9317d0d2Martijn Coenen            return screenUnlocked ? DISPATCH_UNLOCK : DISPATCH_SUCCESS;
3165ef11b7525885a42e681c31ebe2adb8b9317d0d2Martijn Coenen        }
3175ef11b7525885a42e681c31ebe2adb8b9317d0d2Martijn Coenen
31806486f58530d456a9a8d99bf14936539b3540ddeMartijn Coenen        if (tryPeripheralHandover(message)) {
31906486f58530d456a9a8d99bf14936539b3540ddeMartijn Coenen            if (DBG) Log.i(TAG, "matched BT HANDOVER");
32006486f58530d456a9a8d99bf14936539b3540ddeMartijn Coenen            return screenUnlocked ? DISPATCH_UNLOCK : DISPATCH_SUCCESS;
32106486f58530d456a9a8d99bf14936539b3540ddeMartijn Coenen        }
32206486f58530d456a9a8d99bf14936539b3540ddeMartijn Coenen
32306486f58530d456a9a8d99bf14936539b3540ddeMartijn Coenen        if (NfcWifiProtectedSetup.tryNfcWifiSetup(ndef, mContext)) {
32406486f58530d456a9a8d99bf14936539b3540ddeMartijn Coenen            if (DBG) Log.i(TAG, "matched NFC WPS TOKEN");
32506486f58530d456a9a8d99bf14936539b3540ddeMartijn Coenen            return screenUnlocked ? DISPATCH_UNLOCK : DISPATCH_SUCCESS;
32606486f58530d456a9a8d99bf14936539b3540ddeMartijn Coenen        }
32706486f58530d456a9a8d99bf14936539b3540ddeMartijn Coenen
32893aa66518ba76514149a749937529095ef8b5761Martijn Coenen        if (provisioningOnly) {
32993aa66518ba76514149a749937529095ef8b5761Martijn Coenen            if (message == null) {
33093aa66518ba76514149a749937529095ef8b5761Martijn Coenen                // We only allow NDEF-message dispatch in provisioning mode
33193aa66518ba76514149a749937529095ef8b5761Martijn Coenen                return DISPATCH_FAIL;
33293aa66518ba76514149a749937529095ef8b5761Martijn Coenen            }
33393aa66518ba76514149a749937529095ef8b5761Martijn Coenen            // Restrict to mime-types in whitelist.
33493aa66518ba76514149a749937529095ef8b5761Martijn Coenen            String ndefMimeType = message.getRecords()[0].toMimeType();
33593aa66518ba76514149a749937529095ef8b5761Martijn Coenen            if (provisioningMimes == null ||
33693aa66518ba76514149a749937529095ef8b5761Martijn Coenen                    !(Arrays.asList(provisioningMimes).contains(ndefMimeType))) {
33793aa66518ba76514149a749937529095ef8b5761Martijn Coenen                Log.e(TAG, "Dropping NFC intent in provisioning mode.");
33893aa66518ba76514149a749937529095ef8b5761Martijn Coenen                return DISPATCH_FAIL;
33993aa66518ba76514149a749937529095ef8b5761Martijn Coenen            }
34093aa66518ba76514149a749937529095ef8b5761Martijn Coenen        }
34193aa66518ba76514149a749937529095ef8b5761Martijn Coenen
34293aa66518ba76514149a749937529095ef8b5761Martijn Coenen        if (tryNdef(dispatch, message)) {
34332cdfbf250f2611bb7624c13cfd7d111a847676eAndres Morales            return screenUnlocked ? DISPATCH_UNLOCK : DISPATCH_SUCCESS;
34432cdfbf250f2611bb7624c13cfd7d111a847676eAndres Morales        }
34532cdfbf250f2611bb7624c13cfd7d111a847676eAndres Morales
34632cdfbf250f2611bb7624c13cfd7d111a847676eAndres Morales        if (screenUnlocked) {
34732cdfbf250f2611bb7624c13cfd7d111a847676eAndres Morales            // We only allow NDEF-based mimeType matching in case of an unlock
34832cdfbf250f2611bb7624c13cfd7d111a847676eAndres Morales            return DISPATCH_UNLOCK;
34976a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly        }
35076a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly
35134cfff42771acf6d889dd0e5fedc2c2313d9784aAndres Morales        // Only allow NDEF-based mimeType matching for unlock tags
35232cdfbf250f2611bb7624c13cfd7d111a847676eAndres Morales        if (tryTech(dispatch, tag)) {
35332cdfbf250f2611bb7624c13cfd7d111a847676eAndres Morales            return DISPATCH_SUCCESS;
354391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        }
35576a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly
35632cdfbf250f2611bb7624c13cfd7d111a847676eAndres Morales        dispatch.setTagIntent();
35732cdfbf250f2611bb7624c13cfd7d111a847676eAndres Morales        if (dispatch.tryStartActivity()) {
35832cdfbf250f2611bb7624c13cfd7d111a847676eAndres Morales            if (DBG) Log.i(TAG, "matched TAG");
35932cdfbf250f2611bb7624c13cfd7d111a847676eAndres Morales            return DISPATCH_SUCCESS;
360f955ec07aefda9d15b9a64afabedd8d927e0aff7Martijn Coenen        }
361391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly
362391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        if (DBG) Log.i(TAG, "no match");
36332cdfbf250f2611bb7624c13cfd7d111a847676eAndres Morales        return DISPATCH_FAIL;
364f955ec07aefda9d15b9a64afabedd8d927e0aff7Martijn Coenen    }
365f955ec07aefda9d15b9a64afabedd8d927e0aff7Martijn Coenen
36616ee2fe0e0f6d2f811bd06c37cbd53c8b395e6d6Andres Morales    private boolean handleNfcUnlock(Tag tag) {
36716ee2fe0e0f6d2f811bd06c37cbd53c8b395e6d6Andres Morales        return mNfcUnlockManager.tryUnlock(tag);
36816ee2fe0e0f6d2f811bd06c37cbd53c8b395e6d6Andres Morales    }
36916ee2fe0e0f6d2f811bd06c37cbd53c8b395e6d6Andres Morales
370492469777a9bf75c5f9232256746acdfca3d4cdeBartek Teodorczyk    /**
371492469777a9bf75c5f9232256746acdfca3d4cdeBartek Teodorczyk     * Checks for the presence of a URL stored in a tag with tech NfcBarcode.
372492469777a9bf75c5f9232256746acdfca3d4cdeBartek Teodorczyk     * If found, decodes URL and returns NdefMessage message containing an
373492469777a9bf75c5f9232256746acdfca3d4cdeBartek Teodorczyk     * NdefRecord containing the decoded URL. If not found, returns null.
374492469777a9bf75c5f9232256746acdfca3d4cdeBartek Teodorczyk     *
375492469777a9bf75c5f9232256746acdfca3d4cdeBartek Teodorczyk     * URLs are decoded as follows:
376492469777a9bf75c5f9232256746acdfca3d4cdeBartek Teodorczyk     *
377492469777a9bf75c5f9232256746acdfca3d4cdeBartek Teodorczyk     * Ignore first byte (which is 0x80 ORd with a manufacturer ID, corresponding
378492469777a9bf75c5f9232256746acdfca3d4cdeBartek Teodorczyk     * to ISO/IEC 7816-6).
379492469777a9bf75c5f9232256746acdfca3d4cdeBartek Teodorczyk     * The second byte describes the payload data format. There are four defined data
380492469777a9bf75c5f9232256746acdfca3d4cdeBartek Teodorczyk     * format values that identify URL data. Depending on the data format value, the
381492469777a9bf75c5f9232256746acdfca3d4cdeBartek Teodorczyk     * associated prefix is appended to the URL data:
382492469777a9bf75c5f9232256746acdfca3d4cdeBartek Teodorczyk     *
383492469777a9bf75c5f9232256746acdfca3d4cdeBartek Teodorczyk     * 0x01: URL with "http://www." prefix
384492469777a9bf75c5f9232256746acdfca3d4cdeBartek Teodorczyk     * 0x02: URL with "https://www." prefix
385492469777a9bf75c5f9232256746acdfca3d4cdeBartek Teodorczyk     * 0x03: URL with "http://" prefix
386492469777a9bf75c5f9232256746acdfca3d4cdeBartek Teodorczyk     * 0x04: URL with "https://" prefix
387492469777a9bf75c5f9232256746acdfca3d4cdeBartek Teodorczyk     *
388492469777a9bf75c5f9232256746acdfca3d4cdeBartek Teodorczyk     * Other data format values do not identify URL data and are not handled by this function.
389492469777a9bf75c5f9232256746acdfca3d4cdeBartek Teodorczyk     * URL payload is encoded in US-ASCII, following the limitations defined in RFC3987.
390492469777a9bf75c5f9232256746acdfca3d4cdeBartek Teodorczyk     * see http://www.ietf.org/rfc/rfc3987.txt
391492469777a9bf75c5f9232256746acdfca3d4cdeBartek Teodorczyk     *
392492469777a9bf75c5f9232256746acdfca3d4cdeBartek Teodorczyk     * The final two bytes of a tag with tech NfcBarcode are always reserved for CRC data,
393492469777a9bf75c5f9232256746acdfca3d4cdeBartek Teodorczyk     * and are therefore not part of the payload. They are ignored in the decoding of a URL.
394492469777a9bf75c5f9232256746acdfca3d4cdeBartek Teodorczyk     *
395492469777a9bf75c5f9232256746acdfca3d4cdeBartek Teodorczyk     * The default assumption is that the URL occupies the entire payload of the NfcBarcode
396492469777a9bf75c5f9232256746acdfca3d4cdeBartek Teodorczyk     * ID and all bytes of the NfcBarcode payload are decoded until the CRC (final two bytes)
397492469777a9bf75c5f9232256746acdfca3d4cdeBartek Teodorczyk     * is reached. However, the OPTIONAL early terminator byte 0xfe can be used to signal
398492469777a9bf75c5f9232256746acdfca3d4cdeBartek Teodorczyk     * an early end of the URL. Once this function reaches an early terminator byte 0xfe,
399492469777a9bf75c5f9232256746acdfca3d4cdeBartek Teodorczyk     * URL decoding stops and the NdefMessage is created and returned. Any payload data after
400492469777a9bf75c5f9232256746acdfca3d4cdeBartek Teodorczyk     * the first early terminator byte is ignored for the purposes of URL decoding.
401492469777a9bf75c5f9232256746acdfca3d4cdeBartek Teodorczyk     */
402492469777a9bf75c5f9232256746acdfca3d4cdeBartek Teodorczyk    private NdefMessage decodeNfcBarcodeUri(NfcBarcode nfcBarcode) {
403492469777a9bf75c5f9232256746acdfca3d4cdeBartek Teodorczyk        final byte URI_PREFIX_HTTP_WWW  = (byte) 0x01; // "http://www."
404492469777a9bf75c5f9232256746acdfca3d4cdeBartek Teodorczyk        final byte URI_PREFIX_HTTPS_WWW = (byte) 0x02; // "https://www."
405492469777a9bf75c5f9232256746acdfca3d4cdeBartek Teodorczyk        final byte URI_PREFIX_HTTP      = (byte) 0x03; // "http://"
406492469777a9bf75c5f9232256746acdfca3d4cdeBartek Teodorczyk        final byte URI_PREFIX_HTTPS     = (byte) 0x04; // "https://"
407492469777a9bf75c5f9232256746acdfca3d4cdeBartek Teodorczyk
408492469777a9bf75c5f9232256746acdfca3d4cdeBartek Teodorczyk        NdefMessage message = null;
409492469777a9bf75c5f9232256746acdfca3d4cdeBartek Teodorczyk        byte[] tagId = nfcBarcode.getTag().getId();
410492469777a9bf75c5f9232256746acdfca3d4cdeBartek Teodorczyk        // All tags of NfcBarcode technology and Kovio type have lengths of a multiple of 16 bytes
411492469777a9bf75c5f9232256746acdfca3d4cdeBartek Teodorczyk        if (tagId.length >= 4
412492469777a9bf75c5f9232256746acdfca3d4cdeBartek Teodorczyk                && (tagId[1] == URI_PREFIX_HTTP_WWW || tagId[1] == URI_PREFIX_HTTPS_WWW
413492469777a9bf75c5f9232256746acdfca3d4cdeBartek Teodorczyk                    || tagId[1] == URI_PREFIX_HTTP || tagId[1] == URI_PREFIX_HTTPS)) {
414492469777a9bf75c5f9232256746acdfca3d4cdeBartek Teodorczyk            // Look for optional URI terminator (0xfe), used to indicate the end of a URI prior to
415492469777a9bf75c5f9232256746acdfca3d4cdeBartek Teodorczyk            // the end of the full NfcBarcode payload. No terminator means that the URI occupies the
416492469777a9bf75c5f9232256746acdfca3d4cdeBartek Teodorczyk            // entire length of the payload field. Exclude checking the CRC in the final two bytes
417492469777a9bf75c5f9232256746acdfca3d4cdeBartek Teodorczyk            // of the NfcBarcode tagId.
418492469777a9bf75c5f9232256746acdfca3d4cdeBartek Teodorczyk            int end = 2;
419492469777a9bf75c5f9232256746acdfca3d4cdeBartek Teodorczyk            for (; end < tagId.length - 2; end++) {
420492469777a9bf75c5f9232256746acdfca3d4cdeBartek Teodorczyk                if (tagId[end] == (byte) 0xfe) {
421492469777a9bf75c5f9232256746acdfca3d4cdeBartek Teodorczyk                    break;
422492469777a9bf75c5f9232256746acdfca3d4cdeBartek Teodorczyk                }
423492469777a9bf75c5f9232256746acdfca3d4cdeBartek Teodorczyk            }
424492469777a9bf75c5f9232256746acdfca3d4cdeBartek Teodorczyk            byte[] payload = new byte[end - 1]; // Skip also first byte (manufacturer ID)
425492469777a9bf75c5f9232256746acdfca3d4cdeBartek Teodorczyk            System.arraycopy(tagId, 1, payload, 0, payload.length);
426492469777a9bf75c5f9232256746acdfca3d4cdeBartek Teodorczyk            NdefRecord uriRecord = new NdefRecord(
427492469777a9bf75c5f9232256746acdfca3d4cdeBartek Teodorczyk                    NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_URI, tagId, payload);
428492469777a9bf75c5f9232256746acdfca3d4cdeBartek Teodorczyk            message = new NdefMessage(uriRecord);
429492469777a9bf75c5f9232256746acdfca3d4cdeBartek Teodorczyk        }
430492469777a9bf75c5f9232256746acdfca3d4cdeBartek Teodorczyk        return message;
431492469777a9bf75c5f9232256746acdfca3d4cdeBartek Teodorczyk    }
432492469777a9bf75c5f9232256746acdfca3d4cdeBartek Teodorczyk
433391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly    boolean tryOverrides(DispatchInfo dispatch, Tag tag, NdefMessage message, PendingIntent overrideIntent,
434391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            IntentFilter[] overrideFilters, String[][] overrideTechLists) {
435391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        if (overrideIntent == null) {
436391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            return false;
437391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        }
43876a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly        Intent intent;
43976a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly
440391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        // NDEF
441391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        if (message != null) {
442391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            intent = dispatch.setNdefIntent();
443bfaee054834ade31c52002f00553c63701f1c0b0Martijn Coenen            if (intent != null &&
444bfaee054834ade31c52002f00553c63701f1c0b0Martijn Coenen                    isFilterMatch(intent, overrideFilters, overrideTechLists != null)) {
445391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly                try {
446391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly                    overrideIntent.send(mContext, Activity.RESULT_OK, intent);
447391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly                    if (DBG) Log.i(TAG, "matched NDEF override");
448391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly                    return true;
449391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly                } catch (CanceledException e) {
450391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly                    return false;
45176a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly                }
45276a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly            }
45376a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly        }
45476a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly
455391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        // TECH
456391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        intent = dispatch.setTechIntent();
457391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        if (isTechMatch(tag, overrideTechLists)) {
458391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            try {
459391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly                overrideIntent.send(mContext, Activity.RESULT_OK, intent);
460391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly                if (DBG) Log.i(TAG, "matched TECH override");
461391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly                return true;
462391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            } catch (CanceledException e) {
463391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly                return false;
46476a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly            }
465391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        }
466391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly
467391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        // TAG
468391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        intent = dispatch.setTagIntent();
469391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        if (isFilterMatch(intent, overrideFilters, overrideTechLists != null)) {
470391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            try {
471391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly                overrideIntent.send(mContext, Activity.RESULT_OK, intent);
472391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly                if (DBG) Log.i(TAG, "matched TAG override");
473391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly                return true;
474391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            } catch (CanceledException e) {
475391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly                return false;
47676a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly            }
477391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        }
478391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        return false;
479391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly    }
48076a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly
481391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly    boolean isFilterMatch(Intent intent, IntentFilter[] filters, boolean hasTechFilter) {
482391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        if (filters != null) {
483391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            for (IntentFilter filter : filters) {
484391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly                if (filter.match(mContentResolver, intent, false, TAG) >= 0) {
48576a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly                    return true;
48676a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly                }
48776a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly            }
488391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        } else if (!hasTechFilter) {
489391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            return true;  // always match if both filters and techlists are null
49076a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly        }
491391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        return false;
492391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly    }
49376a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly
494391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly    boolean isTechMatch(Tag tag, String[][] techLists) {
495391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        if (techLists == null) {
49676a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly            return false;
49776a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly        }
498391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly
499391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        String[] tagTechs = tag.getTechList();
500391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        Arrays.sort(tagTechs);
501391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        for (String[] filterTechs : techLists) {
502391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            if (filterMatch(tagTechs, filterTechs)) {
503391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly                return true;
504391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            }
505391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        }
506391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        return false;
50776a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly    }
50876a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly
50993aa66518ba76514149a749937529095ef8b5761Martijn Coenen    boolean tryNdef(DispatchInfo dispatch, NdefMessage message) {
510391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        if (message == null) {
511391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            return false;
512391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        }
513525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project        Intent intent = dispatch.setNdefIntent();
514525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project
515525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project        // Bail out if the intent does not contain filterable NDEF data
516525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project        if (intent == null) return false;
517391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly
518391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        // Try to start AAR activity with matching filter
519391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        List<String> aarPackages = extractAarPackages(message);
520391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        for (String pkg : aarPackages) {
521391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            dispatch.intent.setPackage(pkg);
522391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            if (dispatch.tryStartActivity()) {
523391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly                if (DBG) Log.i(TAG, "matched AAR to NDEF");
524391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly                return true;
525391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            }
526391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        }
527391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly
528391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        // Try to perform regular launch of the first AAR
529391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        if (aarPackages.size() > 0) {
530391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            String firstPackage = aarPackages.get(0);
531525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project            PackageManager pm;
532525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project            try {
533525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project                UserHandle currentUser = new UserHandle(ActivityManager.getCurrentUser());
534525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project                pm = mContext.createPackageContextAsUser("android", 0,
535525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project                        currentUser).getPackageManager();
536525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project            } catch (NameNotFoundException e) {
537525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project                Log.e(TAG, "Could not create user package context");
538525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project                return false;
539525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project            }
540525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project            Intent appLaunchIntent = pm.getLaunchIntentForPackage(firstPackage);
541391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            if (appLaunchIntent != null && dispatch.tryStartActivity(appLaunchIntent)) {
542391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly                if (DBG) Log.i(TAG, "matched AAR to application launch");
543391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly                return true;
544391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            }
545f955ec07aefda9d15b9a64afabedd8d927e0aff7Martijn Coenen            // Find the package in Market:
546391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            Intent marketIntent = getAppSearchIntent(firstPackage);
547391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            if (marketIntent != null && dispatch.tryStartActivity(marketIntent)) {
548391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly                if (DBG) Log.i(TAG, "matched AAR to market launch");
549391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly                return true;
550391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            }
551391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        }
552391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly
553391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        // regular launch
554391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        dispatch.intent.setPackage(null);
555e594363266714de153e5b07f050a05acb2ad6ff9Antony Sargent
556e594363266714de153e5b07f050a05acb2ad6ff9Antony Sargent        if (dispatch.isWebIntent()) {
557e594363266714de153e5b07f050a05acb2ad6ff9Antony Sargent            if (DBG) Log.i(TAG, "matched Web link - prompting user");
558e594363266714de153e5b07f050a05acb2ad6ff9Antony Sargent            showWebLinkConfirmation(dispatch);
559e594363266714de153e5b07f050a05acb2ad6ff9Antony Sargent            return true;
560e594363266714de153e5b07f050a05acb2ad6ff9Antony Sargent        }
561e594363266714de153e5b07f050a05acb2ad6ff9Antony Sargent
562391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        if (dispatch.tryStartActivity()) {
563391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            if (DBG) Log.i(TAG, "matched NDEF");
564391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            return true;
565391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        }
566391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly
567391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        return false;
568391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly    }
569391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly
570391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly    static List<String> extractAarPackages(NdefMessage message) {
571391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        List<String> aarPackages = new LinkedList<String>();
572391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        for (NdefRecord record : message.getRecords()) {
573391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            String pkg = checkForAar(record);
574391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            if (pkg != null) {
575391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly                aarPackages.add(pkg);
576391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            }
577222b8c18c4a9cf800f2dd615ef116f796ef7942cMartijn Coenen        }
578391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        return aarPackages;
579222b8c18c4a9cf800f2dd615ef116f796ef7942cMartijn Coenen    }
580222b8c18c4a9cf800f2dd615ef116f796ef7942cMartijn Coenen
581391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly    boolean tryTech(DispatchInfo dispatch, Tag tag) {
582391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        dispatch.setTechIntent();
583391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly
584391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        String[] tagTechs = tag.getTechList();
585391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        Arrays.sort(tagTechs);
586391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly
587391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        // Standard tech dispatch path
588391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        ArrayList<ResolveInfo> matches = new ArrayList<ResolveInfo>();
589391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        List<ComponentInfo> registered = mTechListFilters.getComponents();
590391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly
591525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project        PackageManager pm;
592525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project        try {
593525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project            UserHandle currentUser = new UserHandle(ActivityManager.getCurrentUser());
594525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project            pm = mContext.createPackageContextAsUser("android", 0,
595525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project                    currentUser).getPackageManager();
596525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project        } catch (NameNotFoundException e) {
597525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project            Log.e(TAG, "Could not create user package context");
598525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project            return false;
599525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project        }
600391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        // Check each registered activity to see if it matches
601391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        for (ComponentInfo info : registered) {
602391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            // Don't allow wild card matching
603391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            if (filterMatch(tagTechs, info.techs) &&
604525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project                    isComponentEnabled(pm, info.resolveInfo)) {
605391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly                // Add the activity as a match if it's not already in the list
606391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly                if (!matches.contains(info.resolveInfo)) {
607391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly                    matches.add(info.resolveInfo);
60876a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly                }
60976a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly            }
610391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        }
61176a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly
612391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        if (matches.size() == 1) {
613391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            // Single match, launch directly
614391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            ResolveInfo info = matches.get(0);
615391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            dispatch.intent.setClassName(info.activityInfo.packageName, info.activityInfo.name);
616391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            if (dispatch.tryStartActivity()) {
617391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly                if (DBG) Log.i(TAG, "matched single TECH");
61876a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly                return true;
61976a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly            }
6207d8987f233985a5ff29226890e11012275d325f5Martijn Coenen            dispatch.intent.setComponent(null);
621391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        } else if (matches.size() > 1) {
622391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            // Multiple matches, show a custom activity chooser dialog
623391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            Intent intent = new Intent(mContext, TechListChooserActivity.class);
624391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            intent.putExtra(Intent.EXTRA_INTENT, dispatch.intent);
625391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            intent.putParcelableArrayListExtra(TechListChooserActivity.EXTRA_RESOLVE_INFOS,
626391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly                    matches);
627391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            if (dispatch.tryStartActivity(intent)) {
628391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly                if (DBG) Log.i(TAG, "matched multiple TECH");
629391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly                return true;
630b442e5fc49f235c228d3989c73e333d58caa2adeBen Dodson            }
63176a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly        }
632391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        return false;
63376a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly    }
63476a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly
635b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales    public boolean tryPeripheralHandover(NdefMessage m) {
636b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales        if (m == null || !mDeviceSupportsBluetooth) return false;
637b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales
638b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales        if (DBG) Log.d(TAG, "tryHandover(): " + m.toString());
639b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales
640b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales        HandoverDataParser.BluetoothHandoverData handover = mHandoverDataParser.parseBluetooth(m);
641b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales        if (handover == null || !handover.valid) return false;
64238609ffd78908af4d6bd5156e6b297b357c0dda6Nicolas Prevot        if (UserManager.get(mContext).hasUserRestriction(
64338609ffd78908af4d6bd5156e6b297b357c0dda6Nicolas Prevot                UserManager.DISALLOW_CONFIG_BLUETOOTH,
64438609ffd78908af4d6bd5156e6b297b357c0dda6Nicolas Prevot                // hasUserRestriction does not support UserHandle.CURRENT
64538609ffd78908af4d6bd5156e6b297b357c0dda6Nicolas Prevot                UserHandle.of(ActivityManager.getCurrentUser()))) {
64638609ffd78908af4d6bd5156e6b297b357c0dda6Nicolas Prevot            return false;
64738609ffd78908af4d6bd5156e6b297b357c0dda6Nicolas Prevot        }
648b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales
649b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales        Intent intent = new Intent(mContext, PeripheralHandoverService.class);
650b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales        intent.putExtra(PeripheralHandoverService.EXTRA_PERIPHERAL_DEVICE, handover.device);
651b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales        intent.putExtra(PeripheralHandoverService.EXTRA_PERIPHERAL_NAME, handover.name);
652b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales        intent.putExtra(PeripheralHandoverService.EXTRA_PERIPHERAL_TRANSPORT, handover.transport);
653ca75f7282125255caee81f4bcd8f765d709acc38Jakub Pawlowski        if (handover.oobData != null) {
654ca75f7282125255caee81f4bcd8f765d709acc38Jakub Pawlowski            intent.putExtra(PeripheralHandoverService.EXTRA_PERIPHERAL_OOB_DATA, handover.oobData);
655ca75f7282125255caee81f4bcd8f765d709acc38Jakub Pawlowski        }
6567490b54650e48bbe3e0da8e142f83c4cc63ddc49Hiroki Yamamoto        if (handover.uuids != null) {
6577490b54650e48bbe3e0da8e142f83c4cc63ddc49Hiroki Yamamoto            intent.putExtra(PeripheralHandoverService.EXTRA_PERIPHERAL_UUIDS, handover.uuids);
6587490b54650e48bbe3e0da8e142f83c4cc63ddc49Hiroki Yamamoto        }
6597490b54650e48bbe3e0da8e142f83c4cc63ddc49Hiroki Yamamoto        if (handover.btClass != null) {
6607490b54650e48bbe3e0da8e142f83c4cc63ddc49Hiroki Yamamoto            intent.putExtra(PeripheralHandoverService.EXTRA_PERIPHERAL_CLASS, handover.btClass);
6617490b54650e48bbe3e0da8e142f83c4cc63ddc49Hiroki Yamamoto        }
662b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales        mContext.startServiceAsUser(intent, UserHandle.CURRENT);
663b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales
664b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales        return true;
665b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales    }
666b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales
667b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales
6688e85fe445d229fba5d101b4f95fe0dfa34a8601emike wakerly    /**
6698e85fe445d229fba5d101b4f95fe0dfa34a8601emike wakerly     * Tells the ActivityManager to resume allowing app switches.
6708e85fe445d229fba5d101b4f95fe0dfa34a8601emike wakerly     *
6718e85fe445d229fba5d101b4f95fe0dfa34a8601emike wakerly     * If the current app called stopAppSwitches() then our startActivity() can
6728e85fe445d229fba5d101b4f95fe0dfa34a8601emike wakerly     * be delayed for several seconds. This happens with the default home
6738e85fe445d229fba5d101b4f95fe0dfa34a8601emike wakerly     * screen.  As a system service we can override this behavior with
6748e85fe445d229fba5d101b4f95fe0dfa34a8601emike wakerly     * resumeAppSwitches().
6758e85fe445d229fba5d101b4f95fe0dfa34a8601emike wakerly    */
6768e85fe445d229fba5d101b4f95fe0dfa34a8601emike wakerly    void resumeAppSwitches() {
6778e85fe445d229fba5d101b4f95fe0dfa34a8601emike wakerly        try {
6788e85fe445d229fba5d101b4f95fe0dfa34a8601emike wakerly            mIActivityManager.resumeAppSwitches();
6798e85fe445d229fba5d101b4f95fe0dfa34a8601emike wakerly        } catch (RemoteException e) { }
6808e85fe445d229fba5d101b4f95fe0dfa34a8601emike wakerly    }
6818e85fe445d229fba5d101b4f95fe0dfa34a8601emike wakerly
68276a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly    /** Returns true if the tech list filter matches the techs on the tag */
683391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly    boolean filterMatch(String[] tagTechs, String[] filterTechs) {
68476a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly        if (filterTechs == null || filterTechs.length == 0) return false;
68576a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly
68676a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly        for (String tech : filterTechs) {
68776a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly            if (Arrays.binarySearch(tagTechs, tech) < 0) {
68876a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly                return false;
68976a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly            }
69076a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly        }
69176a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly        return true;
69276a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly    }
69376a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly
694391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly    static String checkForAar(NdefRecord record) {
695391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        if (record.getTnf() == NdefRecord.TNF_EXTERNAL_TYPE &&
696391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly                Arrays.equals(record.getType(), NdefRecord.RTD_ANDROID_APP)) {
697893bc90a71883f8d2bfc82818abbf21c9745efdaElliott Hughes            return new String(record.getPayload(), StandardCharsets.US_ASCII);
69876a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly        }
699391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        return null;
70076a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly    }
70176a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly
702b442e5fc49f235c228d3989c73e333d58caa2adeBen Dodson    /**
703b442e5fc49f235c228d3989c73e333d58caa2adeBen Dodson     * Returns an intent that can be used to find an application not currently
704b442e5fc49f235c228d3989c73e333d58caa2adeBen Dodson     * installed on the device.
705b442e5fc49f235c228d3989c73e333d58caa2adeBen Dodson     */
706391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly    static Intent getAppSearchIntent(String pkg) {
707b442e5fc49f235c228d3989c73e333d58caa2adeBen Dodson        Intent market = new Intent(Intent.ACTION_VIEW);
708b442e5fc49f235c228d3989c73e333d58caa2adeBen Dodson        market.setData(Uri.parse("market://details?id=" + pkg));
709b442e5fc49f235c228d3989c73e333d58caa2adeBen Dodson        return market;
710b442e5fc49f235c228d3989c73e333d58caa2adeBen Dodson    }
711b442e5fc49f235c228d3989c73e333d58caa2adeBen Dodson
712391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly    static boolean isComponentEnabled(PackageManager pm, ResolveInfo info) {
71376a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly        boolean enabled = false;
71476a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly        ComponentName compname = new ComponentName(
71576a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly                info.activityInfo.packageName, info.activityInfo.name);
71676a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly        try {
71776a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly            // Note that getActivityInfo() will internally call
71876a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly            // isEnabledLP() to determine whether the component
71976a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly            // enabled. If it's not, null is returned.
72076a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly            if (pm.getActivityInfo(compname,0) != null) {
72176a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly                enabled = true;
72276a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly            }
72376a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly        } catch (PackageManager.NameNotFoundException e) {
72476a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly            enabled = false;
72576a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly        }
72676a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly        if (!enabled) {
72776a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly            Log.d(TAG, "Component not enabled: " + compname);
72876a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly        }
72976a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly        return enabled;
73076a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly    }
731391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly
732e594363266714de153e5b07f050a05acb2ad6ff9Antony Sargent    void showWebLinkConfirmation(DispatchInfo dispatch) {
733e594363266714de153e5b07f050a05acb2ad6ff9Antony Sargent        if (!mContext.getResources().getBoolean(R.bool.enable_nfc_url_open_dialog)) {
734e594363266714de153e5b07f050a05acb2ad6ff9Antony Sargent            dispatch.tryStartActivity();
735e594363266714de153e5b07f050a05acb2ad6ff9Antony Sargent            return;
736e594363266714de153e5b07f050a05acb2ad6ff9Antony Sargent        }
737e594363266714de153e5b07f050a05acb2ad6ff9Antony Sargent        AlertDialog.Builder builder = new AlertDialog.Builder(
738e594363266714de153e5b07f050a05acb2ad6ff9Antony Sargent                mContext.getApplicationContext(),
739e594363266714de153e5b07f050a05acb2ad6ff9Antony Sargent                android.R.style.Theme_DeviceDefault_Light_Dialog_Alert);
740e594363266714de153e5b07f050a05acb2ad6ff9Antony Sargent        builder.setTitle(R.string.title_confirm_url_open);
741e594363266714de153e5b07f050a05acb2ad6ff9Antony Sargent        LayoutInflater inflater = LayoutInflater.from(mContext);
742e594363266714de153e5b07f050a05acb2ad6ff9Antony Sargent        View view = inflater.inflate(R.layout.url_open_confirmation, null);
743e594363266714de153e5b07f050a05acb2ad6ff9Antony Sargent        if (view != null) {
744e594363266714de153e5b07f050a05acb2ad6ff9Antony Sargent            TextView url = view.findViewById(R.id.url_open_confirmation_link);
745e594363266714de153e5b07f050a05acb2ad6ff9Antony Sargent            if (url != null) {
746e594363266714de153e5b07f050a05acb2ad6ff9Antony Sargent                url.setText(dispatch.getUri());
747e594363266714de153e5b07f050a05acb2ad6ff9Antony Sargent            }
748e594363266714de153e5b07f050a05acb2ad6ff9Antony Sargent            builder.setView(view);
749e594363266714de153e5b07f050a05acb2ad6ff9Antony Sargent        }
750e594363266714de153e5b07f050a05acb2ad6ff9Antony Sargent        builder.setNegativeButton(R.string.cancel, (dialog, which) -> {});
751e594363266714de153e5b07f050a05acb2ad6ff9Antony Sargent        builder.setPositiveButton(R.string.action_confirm_url_open, (dialog, which) -> {
752e594363266714de153e5b07f050a05acb2ad6ff9Antony Sargent            dispatch.tryStartActivity();
753e594363266714de153e5b07f050a05acb2ad6ff9Antony Sargent        });
754e594363266714de153e5b07f050a05acb2ad6ff9Antony Sargent        AlertDialog dialog = builder.create();
755e594363266714de153e5b07f050a05acb2ad6ff9Antony Sargent        dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
756e594363266714de153e5b07f050a05acb2ad6ff9Antony Sargent        dialog.show();
757e594363266714de153e5b07f050a05acb2ad6ff9Antony Sargent    }
758e594363266714de153e5b07f050a05acb2ad6ff9Antony Sargent
759391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly    void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
760391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        synchronized (this) {
761391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            pw.println("mOverrideIntent=" + mOverrideIntent);
762391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            pw.println("mOverrideFilters=" + mOverrideFilters);
763391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly            pw.println("mOverrideTechLists=" + mOverrideTechLists);
764391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly        }
765391cfe2479eca2080c14d1832599ad51cafae918Nick Pelly    }
76676a412f47ff57ce05d84fd51adbf8e72fd37a448Nick Pelly}
767