Loading src/java/android/telephony/SmsManager.java +27 −0 Original line number Diff line number Diff line Loading @@ -1591,6 +1591,33 @@ public final class SmsManager { return null; } /** * Create a single use app specific incoming SMS request for the the calling package. * * This method returns a token that if included in a subsequent incoming SMS message will cause * {@code intent} to be sent with the SMS data. * * The token is only good for one use, after an SMS has been received containing the token all * subsequent SMS messages with the token will be routed as normal. * * An app can only have one request at a time, if the app already has a request pending it will * be replaced with a new request. * * @return Token to include in an SMS message. The token will be 11 characters long. * @see android.provider.Telephony.Sms.Intents#getMessagesFromIntent */ public String createAppSpecificSmsToken(PendingIntent intent) { try { ISms iccSms = getISmsServiceOrThrow(); return iccSms.createAppSpecificSmsToken(getSubscriptionId(), ActivityThread.currentPackageName(), intent); } catch (RemoteException ex) { ex.rethrowFromSystemServer(); return null; } } /** * Filters a bundle to only contain MMS config variables. * Loading src/java/com/android/internal/telephony/AppSmsManager.java 0 → 100644 +177 −0 Original line number Diff line number Diff line /* * Copyright (C) 2016 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.internal.telephony; import android.app.AppOpsManager; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.os.Binder; import android.provider.Telephony.Sms.Intents; import android.telephony.SmsMessage; import android.util.ArrayMap; import android.util.Base64; import android.util.Log; import com.android.internal.annotations.GuardedBy; import java.security.SecureRandom; import java.util.Map; /** * Manager for app specific incoming SMS requests. This can be used to implement SMS based * communication channels (e.g. for SMS based phone number verification) without needing the * {@link Manifest.permission#RECEIVE_SMS} permission. * * {@link #createAppSpecificSmsRequest} allows an application to provide a {@link PendingIntent} * that is triggered when an incoming SMS is received that contains the provided token. */ public class AppSmsManager { private static final String LOG_TAG = "AppSmsManager"; private final SecureRandom mRandom; private final Context mContext; private final Object mLock = new Object(); @GuardedBy("mLock") private final Map<String, AppRequestInfo> mTokenMap; @GuardedBy("mLock") private final Map<String, AppRequestInfo> mPackageMap; public AppSmsManager(Context context) { mRandom = new SecureRandom(); mTokenMap = new ArrayMap<>(); mPackageMap = new ArrayMap<>(); mContext = context; } /** * Create an app specific incoming SMS request for the the calling package. * * This method returns a token that if included in a subsequent incoming SMS message the * {@link Intents.SMS_RECEIVED_ACTION} intent will be delivered only to the calling package and * will not require the application have the {@link Manifest.permission#RECEIVE_SMS} permission. * * An app can only have one request at a time, if the app already has a request it will be * dropped and the new one will be added. * * @return Token to include in an SMS to have it delivered directly to the app. */ public String createAppSpecificSmsToken(String callingPkg, PendingIntent intent) { // Check calling uid matches callingpkg. AppOpsManager appOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE); appOps.checkPackage(Binder.getCallingUid(), callingPkg); // Generate a nonce to store the request under. String token = generateNonce(); synchronized (mLock) { // Only allow one request in flight from a package. if (mPackageMap.containsKey(callingPkg)) { removeRequestLocked(mPackageMap.get(callingPkg)); } // Store state. AppRequestInfo info = new AppRequestInfo(callingPkg, intent, token); addRequestLocked(info); } return token; } /** * Handle an incoming SMS_DELIVER_ACTION intent if it is an app-only SMS. */ public boolean handleSmsReceivedIntent(Intent intent) { // Sanity check the action. if (intent.getAction() != Intents.SMS_DELIVER_ACTION) { Log.wtf(LOG_TAG, "Got intent with incorrect action: " + intent.getAction()); return false; } synchronized (mLock) { AppRequestInfo info = findAppRequestInfoSmsIntentLocked(intent); if (info == null) { // The message didn't contain a token -- nothing to do. return false; } try { Intent fillIn = new Intent(); fillIn.putExtras(intent.getExtras()); info.pendingIntent.send(mContext, 0, fillIn); } catch (PendingIntent.CanceledException e) { // The pending intent is canceled, send this SMS as normal. removeRequestLocked(info); return false; } removeRequestLocked(info); return true; } } private AppRequestInfo findAppRequestInfoSmsIntentLocked(Intent intent) { SmsMessage[] messages = Intents.getMessagesFromIntent(intent); if (messages == null) { return null; } StringBuilder fullMessageBuilder = new StringBuilder(); for (SmsMessage message : messages) { if (message.getMessageBody() == null) { continue; } fullMessageBuilder.append(message.getMessageBody()); } String fullMessage = fullMessageBuilder.toString(); // Look for any tokens in the full message. for (String token : mTokenMap.keySet()) { if (fullMessage.contains(token)) { return mTokenMap.get(token); } } return null; } private String generateNonce() { byte[] bytes = new byte[8]; mRandom.nextBytes(bytes); return Base64.encodeToString(bytes, Base64.URL_SAFE | Base64.NO_WRAP | Base64.NO_PADDING); } private void removeRequestLocked(AppRequestInfo info) { mTokenMap.remove(info.token); mPackageMap.remove(info.packageName); } private void addRequestLocked(AppRequestInfo info) { mTokenMap.put(info.token, info); mPackageMap.put(info.packageName, info); } private final class AppRequestInfo { public final String packageName; public final PendingIntent pendingIntent; public final String token; AppRequestInfo(String packageName, PendingIntent pendingIntent, String token) { this.packageName = packageName; this.pendingIntent = pendingIntent; this.token = token; } } } src/java/com/android/internal/telephony/InboundSmsHandler.java +17 −8 Original line number Diff line number Diff line Loading @@ -42,7 +42,6 @@ import android.content.pm.UserInfo; import android.database.Cursor; import android.database.SQLException; import android.net.Uri; import android.os.storage.StorageManager; import android.os.AsyncResult; import android.os.Binder; import android.os.Build; Loading @@ -54,6 +53,7 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; import android.os.UserManager; import android.os.storage.StorageManager; import android.provider.Telephony; import android.provider.Telephony.Sms.Intents; import android.service.carrier.CarrierMessagingService; Loading Loading @@ -1094,7 +1094,8 @@ public abstract class InboundSmsHandler extends StateMachine { } /** * Creates and dispatches the intent to the default SMS app or the appropriate port. * Creates and dispatches the intent to the default SMS app, appropriate port or via the {@link * AppSmsManager}. * * @param pdus message pdus * @param format the message format, typically "3gpp" or "3gpp2" Loading @@ -1102,7 +1103,7 @@ public abstract class InboundSmsHandler extends StateMachine { * @param resultReceiver the receiver handling the delivery result */ private void dispatchSmsDeliveryIntent(byte[][] pdus, String format, int destPort, BroadcastReceiver resultReceiver) { SmsBroadcastReceiver resultReceiver) { Intent intent = new Intent(); intent.putExtra("pdus", pdus); intent.putExtra("format", format); Loading Loading @@ -1136,11 +1137,17 @@ public abstract class InboundSmsHandler extends StateMachine { intent.setData(uri); intent.setComponent(null); } // Handle app specific sms messages. AppSmsManager appManager = mPhone.getAppSmsManager(); if (appManager.handleSmsReceivedIntent(intent)) { // The AppSmsManager handled this intent, we're done. dropSms(resultReceiver); } else { Bundle options = handleSmsWhitelisting(intent.getComponent()); dispatchIntent(intent, android.Manifest.permission.RECEIVE_SMS, AppOpsManager.OP_RECEIVE_SMS, options, resultReceiver, UserHandle.SYSTEM); } } /** * Function to check if message should be dropped because same message has already been Loading Loading @@ -1286,8 +1293,10 @@ public abstract class InboundSmsHandler extends StateMachine { intent.setComponent(null); // All running users will be notified of the received sms. Bundle options = handleSmsWhitelisting(null); dispatchIntent(intent, android.Manifest.permission.RECEIVE_SMS, AppOpsManager.OP_RECEIVE_SMS, options, this, UserHandle.ALL); AppOpsManager.OP_RECEIVE_SMS, options, this, UserHandle.ALL); } else if (action.equals(Intents.WAP_PUSH_DELIVER_ACTION)) { // Now dispatch the notification only intent intent.setAction(Intents.WAP_PUSH_RECEIVED_ACTION); Loading src/java/com/android/internal/telephony/Phone.java +6 −0 Original line number Diff line number Diff line Loading @@ -241,6 +241,7 @@ public abstract class Phone extends Handler implements PhoneInternalInterface { private boolean mIsVoiceCapable = true; /* Used for communicate between configured CarrierSignalling receivers */ private CarrierSignalAgent mCarrierSignalAgent; private final AppSmsManager mAppSmsManager; // Keep track of whether or not the phone is in Emergency Callback Mode for Phone and // subclasses Loading Loading @@ -447,6 +448,7 @@ public abstract class Phone extends Handler implements PhoneInternalInterface { mCarrierSignalAgent = new CarrierSignalAgent(this); mActionDetached = this.getClass().getPackage().getName() + ".action_detached"; mActionAttached = this.getClass().getPackage().getName() + ".action_attached"; mAppSmsManager = telephonyComponentFactory.makeAppSmsManager(context); if (Build.IS_DEBUGGABLE) { mTelephonyTester = new TelephonyTester(this); Loading Loading @@ -3380,6 +3382,10 @@ public abstract class Phone extends Handler implements PhoneInternalInterface { return null; } public AppSmsManager getAppSmsManager() { return mAppSmsManager; } public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println("Phone: subId=" + getSubId()); pw.println(" mPhoneId=" + mPhoneId); Loading src/java/com/android/internal/telephony/TelephonyComponentFactory.java +7 −3 Original line number Diff line number Diff line Loading @@ -20,17 +20,14 @@ import android.content.Context; import android.database.Cursor; import android.os.Handler; import android.os.IDeviceIdleController; import android.os.PowerManager; import android.os.ServiceManager; import com.android.ims.ImsManager; import com.android.internal.telephony.cdma.CdmaSubscriptionSourceManager; import com.android.internal.telephony.cdma.EriManager; import com.android.internal.telephony.dataconnection.DcTracker; import com.android.internal.telephony.imsphone.ImsExternalCallTracker; import com.android.internal.telephony.imsphone.ImsPhone; import com.android.internal.telephony.imsphone.ImsPhoneCallTracker; import com.android.internal.telephony.imsphone.ImsPullCall; import com.android.internal.telephony.uicc.IccCardProxy; /** Loading Loading @@ -123,6 +120,13 @@ public class TelephonyComponentFactory { return new ImsExternalCallTracker(imsPhone); } /** * Create an AppSmsManager for per-app SMS message. */ public AppSmsManager makeAppSmsManager(Context context) { return new AppSmsManager(context); } public CdmaSubscriptionSourceManager getCdmaSubscriptionSourceManagerInstance(Context context, CommandsInterface ci, Handler h, int what, Object obj) { Loading Loading
src/java/android/telephony/SmsManager.java +27 −0 Original line number Diff line number Diff line Loading @@ -1591,6 +1591,33 @@ public final class SmsManager { return null; } /** * Create a single use app specific incoming SMS request for the the calling package. * * This method returns a token that if included in a subsequent incoming SMS message will cause * {@code intent} to be sent with the SMS data. * * The token is only good for one use, after an SMS has been received containing the token all * subsequent SMS messages with the token will be routed as normal. * * An app can only have one request at a time, if the app already has a request pending it will * be replaced with a new request. * * @return Token to include in an SMS message. The token will be 11 characters long. * @see android.provider.Telephony.Sms.Intents#getMessagesFromIntent */ public String createAppSpecificSmsToken(PendingIntent intent) { try { ISms iccSms = getISmsServiceOrThrow(); return iccSms.createAppSpecificSmsToken(getSubscriptionId(), ActivityThread.currentPackageName(), intent); } catch (RemoteException ex) { ex.rethrowFromSystemServer(); return null; } } /** * Filters a bundle to only contain MMS config variables. * Loading
src/java/com/android/internal/telephony/AppSmsManager.java 0 → 100644 +177 −0 Original line number Diff line number Diff line /* * Copyright (C) 2016 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.internal.telephony; import android.app.AppOpsManager; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.os.Binder; import android.provider.Telephony.Sms.Intents; import android.telephony.SmsMessage; import android.util.ArrayMap; import android.util.Base64; import android.util.Log; import com.android.internal.annotations.GuardedBy; import java.security.SecureRandom; import java.util.Map; /** * Manager for app specific incoming SMS requests. This can be used to implement SMS based * communication channels (e.g. for SMS based phone number verification) without needing the * {@link Manifest.permission#RECEIVE_SMS} permission. * * {@link #createAppSpecificSmsRequest} allows an application to provide a {@link PendingIntent} * that is triggered when an incoming SMS is received that contains the provided token. */ public class AppSmsManager { private static final String LOG_TAG = "AppSmsManager"; private final SecureRandom mRandom; private final Context mContext; private final Object mLock = new Object(); @GuardedBy("mLock") private final Map<String, AppRequestInfo> mTokenMap; @GuardedBy("mLock") private final Map<String, AppRequestInfo> mPackageMap; public AppSmsManager(Context context) { mRandom = new SecureRandom(); mTokenMap = new ArrayMap<>(); mPackageMap = new ArrayMap<>(); mContext = context; } /** * Create an app specific incoming SMS request for the the calling package. * * This method returns a token that if included in a subsequent incoming SMS message the * {@link Intents.SMS_RECEIVED_ACTION} intent will be delivered only to the calling package and * will not require the application have the {@link Manifest.permission#RECEIVE_SMS} permission. * * An app can only have one request at a time, if the app already has a request it will be * dropped and the new one will be added. * * @return Token to include in an SMS to have it delivered directly to the app. */ public String createAppSpecificSmsToken(String callingPkg, PendingIntent intent) { // Check calling uid matches callingpkg. AppOpsManager appOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE); appOps.checkPackage(Binder.getCallingUid(), callingPkg); // Generate a nonce to store the request under. String token = generateNonce(); synchronized (mLock) { // Only allow one request in flight from a package. if (mPackageMap.containsKey(callingPkg)) { removeRequestLocked(mPackageMap.get(callingPkg)); } // Store state. AppRequestInfo info = new AppRequestInfo(callingPkg, intent, token); addRequestLocked(info); } return token; } /** * Handle an incoming SMS_DELIVER_ACTION intent if it is an app-only SMS. */ public boolean handleSmsReceivedIntent(Intent intent) { // Sanity check the action. if (intent.getAction() != Intents.SMS_DELIVER_ACTION) { Log.wtf(LOG_TAG, "Got intent with incorrect action: " + intent.getAction()); return false; } synchronized (mLock) { AppRequestInfo info = findAppRequestInfoSmsIntentLocked(intent); if (info == null) { // The message didn't contain a token -- nothing to do. return false; } try { Intent fillIn = new Intent(); fillIn.putExtras(intent.getExtras()); info.pendingIntent.send(mContext, 0, fillIn); } catch (PendingIntent.CanceledException e) { // The pending intent is canceled, send this SMS as normal. removeRequestLocked(info); return false; } removeRequestLocked(info); return true; } } private AppRequestInfo findAppRequestInfoSmsIntentLocked(Intent intent) { SmsMessage[] messages = Intents.getMessagesFromIntent(intent); if (messages == null) { return null; } StringBuilder fullMessageBuilder = new StringBuilder(); for (SmsMessage message : messages) { if (message.getMessageBody() == null) { continue; } fullMessageBuilder.append(message.getMessageBody()); } String fullMessage = fullMessageBuilder.toString(); // Look for any tokens in the full message. for (String token : mTokenMap.keySet()) { if (fullMessage.contains(token)) { return mTokenMap.get(token); } } return null; } private String generateNonce() { byte[] bytes = new byte[8]; mRandom.nextBytes(bytes); return Base64.encodeToString(bytes, Base64.URL_SAFE | Base64.NO_WRAP | Base64.NO_PADDING); } private void removeRequestLocked(AppRequestInfo info) { mTokenMap.remove(info.token); mPackageMap.remove(info.packageName); } private void addRequestLocked(AppRequestInfo info) { mTokenMap.put(info.token, info); mPackageMap.put(info.packageName, info); } private final class AppRequestInfo { public final String packageName; public final PendingIntent pendingIntent; public final String token; AppRequestInfo(String packageName, PendingIntent pendingIntent, String token) { this.packageName = packageName; this.pendingIntent = pendingIntent; this.token = token; } } }
src/java/com/android/internal/telephony/InboundSmsHandler.java +17 −8 Original line number Diff line number Diff line Loading @@ -42,7 +42,6 @@ import android.content.pm.UserInfo; import android.database.Cursor; import android.database.SQLException; import android.net.Uri; import android.os.storage.StorageManager; import android.os.AsyncResult; import android.os.Binder; import android.os.Build; Loading @@ -54,6 +53,7 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; import android.os.UserManager; import android.os.storage.StorageManager; import android.provider.Telephony; import android.provider.Telephony.Sms.Intents; import android.service.carrier.CarrierMessagingService; Loading Loading @@ -1094,7 +1094,8 @@ public abstract class InboundSmsHandler extends StateMachine { } /** * Creates and dispatches the intent to the default SMS app or the appropriate port. * Creates and dispatches the intent to the default SMS app, appropriate port or via the {@link * AppSmsManager}. * * @param pdus message pdus * @param format the message format, typically "3gpp" or "3gpp2" Loading @@ -1102,7 +1103,7 @@ public abstract class InboundSmsHandler extends StateMachine { * @param resultReceiver the receiver handling the delivery result */ private void dispatchSmsDeliveryIntent(byte[][] pdus, String format, int destPort, BroadcastReceiver resultReceiver) { SmsBroadcastReceiver resultReceiver) { Intent intent = new Intent(); intent.putExtra("pdus", pdus); intent.putExtra("format", format); Loading Loading @@ -1136,11 +1137,17 @@ public abstract class InboundSmsHandler extends StateMachine { intent.setData(uri); intent.setComponent(null); } // Handle app specific sms messages. AppSmsManager appManager = mPhone.getAppSmsManager(); if (appManager.handleSmsReceivedIntent(intent)) { // The AppSmsManager handled this intent, we're done. dropSms(resultReceiver); } else { Bundle options = handleSmsWhitelisting(intent.getComponent()); dispatchIntent(intent, android.Manifest.permission.RECEIVE_SMS, AppOpsManager.OP_RECEIVE_SMS, options, resultReceiver, UserHandle.SYSTEM); } } /** * Function to check if message should be dropped because same message has already been Loading Loading @@ -1286,8 +1293,10 @@ public abstract class InboundSmsHandler extends StateMachine { intent.setComponent(null); // All running users will be notified of the received sms. Bundle options = handleSmsWhitelisting(null); dispatchIntent(intent, android.Manifest.permission.RECEIVE_SMS, AppOpsManager.OP_RECEIVE_SMS, options, this, UserHandle.ALL); AppOpsManager.OP_RECEIVE_SMS, options, this, UserHandle.ALL); } else if (action.equals(Intents.WAP_PUSH_DELIVER_ACTION)) { // Now dispatch the notification only intent intent.setAction(Intents.WAP_PUSH_RECEIVED_ACTION); Loading
src/java/com/android/internal/telephony/Phone.java +6 −0 Original line number Diff line number Diff line Loading @@ -241,6 +241,7 @@ public abstract class Phone extends Handler implements PhoneInternalInterface { private boolean mIsVoiceCapable = true; /* Used for communicate between configured CarrierSignalling receivers */ private CarrierSignalAgent mCarrierSignalAgent; private final AppSmsManager mAppSmsManager; // Keep track of whether or not the phone is in Emergency Callback Mode for Phone and // subclasses Loading Loading @@ -447,6 +448,7 @@ public abstract class Phone extends Handler implements PhoneInternalInterface { mCarrierSignalAgent = new CarrierSignalAgent(this); mActionDetached = this.getClass().getPackage().getName() + ".action_detached"; mActionAttached = this.getClass().getPackage().getName() + ".action_attached"; mAppSmsManager = telephonyComponentFactory.makeAppSmsManager(context); if (Build.IS_DEBUGGABLE) { mTelephonyTester = new TelephonyTester(this); Loading Loading @@ -3380,6 +3382,10 @@ public abstract class Phone extends Handler implements PhoneInternalInterface { return null; } public AppSmsManager getAppSmsManager() { return mAppSmsManager; } public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println("Phone: subId=" + getSubId()); pw.println(" mPhoneId=" + mPhoneId); Loading
src/java/com/android/internal/telephony/TelephonyComponentFactory.java +7 −3 Original line number Diff line number Diff line Loading @@ -20,17 +20,14 @@ import android.content.Context; import android.database.Cursor; import android.os.Handler; import android.os.IDeviceIdleController; import android.os.PowerManager; import android.os.ServiceManager; import com.android.ims.ImsManager; import com.android.internal.telephony.cdma.CdmaSubscriptionSourceManager; import com.android.internal.telephony.cdma.EriManager; import com.android.internal.telephony.dataconnection.DcTracker; import com.android.internal.telephony.imsphone.ImsExternalCallTracker; import com.android.internal.telephony.imsphone.ImsPhone; import com.android.internal.telephony.imsphone.ImsPhoneCallTracker; import com.android.internal.telephony.imsphone.ImsPullCall; import com.android.internal.telephony.uicc.IccCardProxy; /** Loading Loading @@ -123,6 +120,13 @@ public class TelephonyComponentFactory { return new ImsExternalCallTracker(imsPhone); } /** * Create an AppSmsManager for per-app SMS message. */ public AppSmsManager makeAppSmsManager(Context context) { return new AppSmsManager(context); } public CdmaSubscriptionSourceManager getCdmaSubscriptionSourceManagerInstance(Context context, CommandsInterface ci, Handler h, int what, Object obj) { Loading