| /* |
| * Copyright (C) 2014 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.nfc; |
| |
| import java.util.ArrayList; |
| |
| import android.app.Activity; |
| import android.app.ActivityManager; |
| import android.app.ActivityManagerNative; |
| import android.app.AlertDialog; |
| import android.content.BroadcastReceiver; |
| import android.content.Context; |
| import android.content.DialogInterface; |
| import android.content.ClipData; |
| import android.content.Intent; |
| import android.content.IntentFilter; |
| import android.content.pm.PackageManager; |
| import android.net.Uri; |
| import android.nfc.BeamShareData; |
| import android.nfc.NdefMessage; |
| import android.nfc.NdefRecord; |
| import android.nfc.NfcAdapter; |
| import android.os.Bundle; |
| import android.os.UserHandle; |
| import android.os.RemoteException; |
| import android.util.Log; |
| import android.util.EventLog; |
| import android.webkit.URLUtil; |
| import android.Manifest.permission; |
| |
| import com.android.internal.R; |
| |
| /** |
| * This class is registered by NfcService to handle |
| * ACTION_SHARE intents. It tries to parse data contained |
| * in ACTION_SHARE intents in either a content/file Uri, |
| * which can be sent using NFC handover, or alternatively |
| * it tries to parse texts and URLs to store them in a simple |
| * Text or Uri NdefRecord. The data is then passed on into |
| * NfcService to transmit on NFC tap. |
| * |
| */ |
| public class BeamShareActivity extends Activity { |
| static final String TAG ="BeamShareActivity"; |
| static final boolean DBG = false; |
| |
| ArrayList<Uri> mUris; |
| NdefMessage mNdefMessage; |
| NfcAdapter mNfcAdapter; |
| Intent mLaunchIntent; |
| |
| @Override |
| protected void onCreate(Bundle savedInstanceState) { |
| super.onCreate(savedInstanceState); |
| mUris = new ArrayList<Uri>(); |
| mNdefMessage = null; |
| mNfcAdapter = NfcAdapter.getDefaultAdapter(this); |
| mLaunchIntent = getIntent(); |
| if (mNfcAdapter == null) { |
| Log.e(TAG, "NFC adapter not present."); |
| finish(); |
| } else { |
| if (!mNfcAdapter.isEnabled()) { |
| showNfcDialogAndExit(com.android.nfc.R.string.beam_requires_nfc_enabled); |
| } else { |
| parseShareIntentAndFinish(mLaunchIntent); |
| } |
| } |
| } |
| |
| @Override |
| protected void onDestroy() { |
| try { |
| unregisterReceiver(mReceiver); |
| } catch (Exception e) { |
| Log.w(TAG, e.getMessage()); |
| } |
| super.onDestroy(); |
| } |
| |
| private void showNfcDialogAndExit(int msgId) { |
| IntentFilter filter = new IntentFilter(NfcAdapter.ACTION_ADAPTER_STATE_CHANGED); |
| registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, null); |
| |
| AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this, |
| AlertDialog.THEME_DEVICE_DEFAULT_LIGHT); |
| dialogBuilder.setMessage(msgId); |
| dialogBuilder.setOnCancelListener(new DialogInterface.OnCancelListener() { |
| @Override |
| public void onCancel(DialogInterface dialogInterface) { |
| finish(); |
| } |
| }); |
| dialogBuilder.setPositiveButton(R.string.yes, |
| new DialogInterface.OnClickListener() { |
| @Override |
| public void onClick(DialogInterface dialog, int id) { |
| if (!mNfcAdapter.isEnabled()) { |
| mNfcAdapter.enable(); |
| // Wait for enable broadcast |
| } else { |
| parseShareIntentAndFinish(mLaunchIntent); |
| } |
| } |
| }); |
| dialogBuilder.setNegativeButton(R.string.no, |
| new DialogInterface.OnClickListener() { |
| @Override |
| public void onClick(DialogInterface dialogInterface, int i) { |
| finish(); |
| } |
| }); |
| dialogBuilder.show(); |
| } |
| |
| void tryUri(Uri uri) { |
| if (uri.getScheme().equalsIgnoreCase("content") || |
| uri.getScheme().equalsIgnoreCase("file")) { |
| // Typically larger data, this can be shared using NFC handover |
| mUris.add(uri); |
| } else { |
| // Just put this Uri in an NDEF message |
| mNdefMessage = new NdefMessage(NdefRecord.createUri(uri)); |
| } |
| } |
| |
| void tryText(String text) { |
| if (URLUtil.isValidUrl(text)) { |
| Uri parsedUri = Uri.parse(text); |
| tryUri(parsedUri); |
| } else { |
| mNdefMessage = new NdefMessage(NdefRecord.createTextRecord(null, text)); |
| } |
| } |
| |
| public void parseShareIntentAndFinish(Intent intent) { |
| if (intent == null || intent.getAction() == null || |
| (!intent.getAction().equalsIgnoreCase(Intent.ACTION_SEND) && |
| !intent.getAction().equalsIgnoreCase(Intent.ACTION_SEND_MULTIPLE))) return; |
| |
| // First, see if the intent contains clip-data, and if so get data from there |
| ClipData clipData = intent.getClipData(); |
| if (clipData != null && clipData.getItemCount() > 0) { |
| for (int i = 0; i < clipData.getItemCount(); i++) { |
| ClipData.Item item = clipData.getItemAt(i); |
| // First try to get an Uri |
| Uri uri = item.getUri(); |
| String plainText = null; |
| try { |
| plainText = item.coerceToText(this).toString(); |
| } catch (IllegalStateException e) { |
| if (DBG) Log.d(TAG, e.getMessage()); |
| continue; |
| } |
| if (uri != null) { |
| if (DBG) Log.d(TAG, "Found uri in ClipData."); |
| tryUri(uri); |
| } else if (plainText != null) { |
| if (DBG) Log.d(TAG, "Found text in ClipData."); |
| tryText(plainText); |
| } else { |
| if (DBG) Log.d(TAG, "Did not find any shareable data in ClipData."); |
| } |
| } |
| } else { |
| if (intent.getAction().equalsIgnoreCase(Intent.ACTION_SEND)) { |
| final Uri uri = intent.getParcelableExtra(Intent.EXTRA_STREAM); |
| final CharSequence text = intent.getCharSequenceExtra(Intent.EXTRA_TEXT); |
| if (uri != null) { |
| if (DBG) Log.d(TAG, "Found uri in ACTION_SEND intent."); |
| tryUri(uri); |
| } else if (text != null) { |
| if (DBG) Log.d(TAG, "Found EXTRA_TEXT in ACTION_SEND intent."); |
| tryText(text.toString()); |
| } else { |
| if (DBG) Log.d(TAG, "Did not find any shareable data in ACTION_SEND intent."); |
| } |
| } else { |
| final ArrayList<Uri> uris = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM); |
| final ArrayList<CharSequence> texts = intent.getCharSequenceArrayListExtra( |
| Intent.EXTRA_TEXT); |
| |
| if (uris != null && uris.size() > 0) { |
| for (Uri uri : uris) { |
| if (DBG) Log.d(TAG, "Found uri in ACTION_SEND_MULTIPLE intent."); |
| tryUri(uri); |
| } |
| } else if (texts != null && texts.size() > 0) { |
| // Try EXTRA_TEXT, but just for the first record |
| if (DBG) Log.d(TAG, "Found text in ACTION_SEND_MULTIPLE intent."); |
| tryText(texts.get(0).toString()); |
| } else { |
| if (DBG) Log.d(TAG, "Did not find any shareable data in " + |
| "ACTION_SEND_MULTIPLE intent."); |
| } |
| } |
| } |
| |
| BeamShareData shareData = null; |
| UserHandle myUserHandle = new UserHandle(UserHandle.myUserId()); |
| if (mUris.size() > 0) { |
| // Uris have our first preference for sharing |
| Uri[] uriArray = new Uri[mUris.size()]; |
| int numValidUris = 0; |
| for (Uri uri : mUris) { |
| try { |
| int uid = ActivityManagerNative.getDefault().getLaunchedFromUid(getActivityToken()); |
| if (uri.getScheme().equalsIgnoreCase("file") && |
| getApplicationContext().checkPermission(permission.READ_EXTERNAL_STORAGE, -1, uid) != |
| PackageManager.PERMISSION_GRANTED) { |
| Log.e(TAG, "File based Uri doesn't have External Storage Permission."); |
| EventLog.writeEvent(0x534e4554, "37287958", uid, uri.getPath()); |
| break; |
| } |
| grantUriPermission("com.android.nfc", uri, Intent.FLAG_GRANT_READ_URI_PERMISSION); |
| uriArray[numValidUris++] = uri; |
| if (DBG) Log.d(TAG, "Found uri: " + uri); |
| } catch (SecurityException e) { |
| Log.e(TAG, "Security exception granting uri permission to NFC process."); |
| break; |
| } catch (RemoteException e) { |
| Log.e(TAG, "Remote exception accessing uid of the calling process."); |
| break; |
| } |
| } |
| if (numValidUris != 0 && numValidUris == mUris.size()) { |
| shareData = new BeamShareData(null, uriArray, myUserHandle, 0); |
| } else { |
| // No uris left |
| shareData = new BeamShareData(null, null, myUserHandle, 0); |
| } |
| } else if (mNdefMessage != null) { |
| shareData = new BeamShareData(mNdefMessage, null, myUserHandle, 0); |
| if (DBG) Log.d(TAG, "Created NDEF message:" + mNdefMessage.toString()); |
| } else { |
| if (DBG) Log.d(TAG, "Could not find any data to parse."); |
| // Activity may have set something to share over NFC, so pass on anyway |
| shareData = new BeamShareData(null, null, myUserHandle, 0); |
| } |
| mNfcAdapter.invokeBeam(shareData); |
| finish(); |
| } |
| |
| final BroadcastReceiver mReceiver = new BroadcastReceiver() { |
| @Override |
| public void onReceive(Context context, Intent intent) { |
| String action = intent.getAction(); |
| if (NfcAdapter.ACTION_ADAPTER_STATE_CHANGED.equals(intent.getAction())) { |
| int state = intent.getIntExtra(NfcAdapter.EXTRA_ADAPTER_STATE, |
| NfcAdapter.STATE_OFF); |
| if (state == NfcAdapter.STATE_ON) { |
| parseShareIntentAndFinish(mLaunchIntent); |
| } |
| } |
| } |
| }; |
| } |