Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 08496b0e authored by Hongming Jin's avatar Hongming Jin
Browse files

Add API in SmsManager to return sms messages for financial app.

Test: atest android.telephony.cts.SmsManagerTest
Bug: 111207447
Change-Id: I1571cb005f7c1374a9acbec27041bca291fa7153
parent b20446f6
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -561,6 +561,7 @@ java_defaults {
        "telephony/java/android/telephony/mbms/vendor/IMbmsStreamingService.aidl",
        "telephony/java/android/telephony/mbms/vendor/IMbmsGroupCallService.aidl",
        "telephony/java/android/telephony/ICellInfoCallback.aidl",
        "telephony/java/android/telephony/IFinancialSmsCallback.aidl",
        "telephony/java/android/telephony/INetworkService.aidl",
        "telephony/java/android/telephony/INetworkServiceCallback.aidl",
        "telephony/java/com/android/ims/internal/IImsCallSession.aidl",
+6 −0
Original line number Diff line number Diff line
@@ -44670,6 +44670,7 @@ package android.telephony {
    method public static android.telephony.SmsManager getDefault();
    method public static int getDefaultSmsSubscriptionId();
    method public static android.telephony.SmsManager getSmsManagerForSubscriptionId(int);
    method @RequiresPermission(android.Manifest.permission.SMS_FINANCIAL_TRANSACTIONS) public void getSmsMessagesForFinancialApp(android.os.Bundle, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.SmsManager.FinancialSmsCallback);
    method public int getSubscriptionId();
    method public void injectSmsPdu(byte[], String, android.app.PendingIntent);
    method public void sendDataMessage(String, String, short, byte[], android.app.PendingIntent, android.app.PendingIntent);
@@ -44732,6 +44733,11 @@ package android.telephony {
    field public static final int STATUS_ON_ICC_UNSENT = 7; // 0x7
  }
  public abstract static class SmsManager.FinancialSmsCallback {
    ctor public SmsManager.FinancialSmsCallback();
    method public abstract void onFinancialSmsMessages(android.database.CursorWindow);
  }
  public class SmsMessage {
    method public static int[] calculateLength(CharSequence, boolean);
    method public static int[] calculateLength(String, boolean);
+6 −0
Original line number Diff line number Diff line
@@ -18,6 +18,8 @@ package android.app.role;

import android.app.role.IOnRoleHoldersChangedListener;
import android.app.role.IRoleManagerCallback;
import android.os.Bundle;
import android.telephony.IFinancialSmsCallback;

/**
 * @hide
@@ -52,4 +54,8 @@ interface IRoleManager {
    List<String> getHeldRolesFromController(in String packageName);

    String getDefaultSmsPackage(int userId);
    /**
     * Get filtered SMS messages for financial app.
     */
    void getSmsMessagesForFinancialApp(in String callingPkg, in Bundle params, in IFinancialSmsCallback callback);
}
+218 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 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.server.role;

import android.Manifest;
import android.annotation.MainThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.UserHandle;
import android.service.sms.FinancialSmsService;
import android.service.sms.IFinancialSmsService;
import android.util.Slog;

import com.android.internal.annotations.GuardedBy;

import java.io.PrintWriter;
import java.util.ArrayList;

/**
 * This class binds to {@code FinancialSmsService}.
 */
final class FinancialSmsManager {

    private static final String TAG = "FinancialSmsManager";

    private final Context mContext;
    private final Object mLock = new Object();

    @GuardedBy("mLock")
    private ServiceConnection mServiceConnection;

    @GuardedBy("mLock")
    private IFinancialSmsService mRemoteService;

    @GuardedBy("mLock")
    private ArrayList<Command> mQueuedCommands;

    FinancialSmsManager(Context context) {
        mContext = context;
    }

    @Nullable
    ServiceInfo getServiceInfo() {
        final String packageName =
                mContext.getPackageManager().getServicesSystemSharedLibraryPackageName();
        if (packageName == null) {
            Slog.w(TAG, "no external services package!");
            return null;
        }

        final Intent intent = new Intent(FinancialSmsService.ACTION_FINANCIAL_SERVICE_INTENT);
        intent.setPackage(packageName);
        final ResolveInfo resolveInfo = mContext.getPackageManager().resolveService(intent,
                PackageManager.GET_SERVICES);
        if (resolveInfo == null || resolveInfo.serviceInfo == null) {
            Slog.w(TAG, "No valid components found.");
            return null;
        }
        return resolveInfo.serviceInfo;
    }

    @Nullable
    private ComponentName getServiceComponentName() {
        final ServiceInfo serviceInfo = getServiceInfo();
        if (serviceInfo == null) return null;

        final ComponentName name = new ComponentName(serviceInfo.packageName, serviceInfo.name);
        if (!Manifest.permission.BIND_FINANCIAL_SMS_SERVICE.equals(serviceInfo.permission)) {
            Slog.w(TAG, name.flattenToShortString() + " does not require permission "
                    + Manifest.permission.BIND_FINANCIAL_SMS_SERVICE);
            return null;
        }

        return name;
    }

    void reset() {
        synchronized (mLock) {
            if (mServiceConnection != null) {
                mContext.unbindService(mServiceConnection);
                mServiceConnection = null;
            } else {
                Slog.d(TAG, "reset(): service is not bound. Do nothing.");
            }
        }
    }

    /**
     * Run a command, starting the service connection if necessary.
     */
    private void connectAndRun(@NonNull Command command) {
        synchronized (mLock) {
            if (mRemoteService != null) {
                try {
                    command.run(mRemoteService);
                } catch (RemoteException e) {
                    Slog.w(TAG, "exception calling service: " + e);
                }
                return;
            } else {
                if (mQueuedCommands == null) {
                    mQueuedCommands = new ArrayList<>(1);
                }
                mQueuedCommands.add(command);
                // If we're already connected, don't create a new connection, just leave - the
                // command will be run when the service connects
                if (mServiceConnection != null) return;
            }

            // Create the connection
            mServiceConnection = new ServiceConnection() {
                @Override
                public void onServiceConnected(ComponentName name, IBinder service) {
                    synchronized (mLock) {
                        mRemoteService = IFinancialSmsService.Stub.asInterface(service);
                        if (mQueuedCommands != null) {
                            final int size = mQueuedCommands.size();
                            for (int i = 0; i < size; i++) {
                                final Command queuedCommand = mQueuedCommands.get(i);
                                try {
                                    queuedCommand.run(mRemoteService);
                                } catch (RemoteException e) {
                                    Slog.w(TAG, "exception calling " + name + ": " + e);
                                }
                            }
                            mQueuedCommands = null;
                        }
                    }
                }

                @Override
                @MainThread
                public void onServiceDisconnected(ComponentName name) {
                    synchronized (mLock) {
                        mRemoteService = null;
                    }
                }

                @Override
                public void onBindingDied(ComponentName name) {
                    synchronized (mLock) {
                        mRemoteService = null;
                    }
                }

                @Override
                public void onNullBinding(ComponentName name) {
                    synchronized (mLock) {
                        mRemoteService = null;
                    }
                }
            };

            final ComponentName component = getServiceComponentName();
            if (component != null) {
                final Intent intent = new Intent();
                intent.setComponent(component);
                final long token = Binder.clearCallingIdentity();
                try {
                    mContext.bindServiceAsUser(intent, mServiceConnection, Context.BIND_AUTO_CREATE,
                            UserHandle.getUserHandleForUid(UserHandle.getCallingUserId()));
                } finally {
                    Binder.restoreCallingIdentity(token);
                }
            }
        }
    }

    void getSmsMessages(RemoteCallback callback, @Nullable Bundle params) {
        connectAndRun((service) -> service.getSmsMessages(callback, params));
    }

    void dump(String prefix, PrintWriter pw) {
        final ComponentName impl = getServiceComponentName();
        pw.print(prefix); pw.print("User ID: "); pw.println(UserHandle.getCallingUserId());
        pw.print(prefix); pw.print("Queued commands: ");
        if (mQueuedCommands == null) {
            pw.println("N/A");
        } else {
            pw.println(mQueuedCommands.size());
        }
        pw.print(prefix); pw.print("Implementation: ");
        if (impl == null) {
            pw.println("N/A");
            return;
        }
        pw.println(impl.flattenToShortString());
    }

    private interface Command {
        void run(IFinancialSmsService service) throws RemoteException;
    }
}
+54 −0
Original line number Diff line number Diff line
@@ -33,16 +33,24 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.PermissionChecker;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManagerInternal;
import android.content.pm.Signature;
import android.database.CursorWindow;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.RemoteCallback;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
import android.os.UserHandle;
import android.os.UserManagerInternal;
import android.service.sms.FinancialSmsService;
import android.telephony.IFinancialSmsCallback;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.PackageUtils;
@@ -620,5 +628,51 @@ public class RoleManagerService extends SystemService implements RoleUserState.C

            dumpOutputStream.flush();
        }

        /**
         * Get filtered SMS messages for financial app.
         */
        @Override
        public void getSmsMessagesForFinancialApp(
                String callingPkg, Bundle params, IFinancialSmsCallback callback) {
            int mode = PermissionChecker.checkCallingOrSelfPermission(
                    getContext(),
                    AppOpsManager.OPSTR_SMS_FINANCIAL_TRANSACTIONS);

            if (mode == PermissionChecker.PERMISSION_GRANTED) {
                FinancialSmsManager financialSmsManager = new FinancialSmsManager(getContext());
                financialSmsManager.getSmsMessages(new RemoteCallback((result) -> {
                    CursorWindow messages = null;
                    if (result == null) {
                        Slog.w(LOG_TAG, "result is null.");
                    } else {
                        messages = result.getParcelable(FinancialSmsService.EXTRA_SMS_MSGS);
                    }
                    try {
                        callback.onGetSmsMessagesForFinancialApp(messages);
                    } catch (RemoteException e) {
                        // do nothing
                    }
                }), params);
            } else {
                try {
                    callback.onGetSmsMessagesForFinancialApp(null);
                } catch (RemoteException e) {
                    // do nothing
                }
            }
        }

        private int getUidForPackage(String packageName) {
            long ident = Binder.clearCallingIdentity();
            try {
                return getContext().getPackageManager().getApplicationInfo(packageName,
                        PackageManager.MATCH_ANY_USER).uid;
            } catch (NameNotFoundException nnfe) {
                return -1;
            } finally {
                Binder.restoreCallingIdentity(ident);
            }
        }
    }
}
Loading