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

Commit 44d066c0 authored by Tony Zhu's avatar Tony Zhu Committed by android-build-merger
Browse files

Merge "[Call Screening]Programmatically changing the default call screening app."

am: 3c7dc0c9

Change-Id: Ifb438f6f85084f896a0984f74c5034b3fb59921a
parents 4980d238 3c7dc0c9
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -296,6 +296,15 @@
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>

        <activity android:name="com.android.server.telecom.components.ChangeDefaultCallScreeningApp"
            android:label="@string/change_default_dialer_dialog_title"
            android:excludeFromRecents="true"
            android:theme="@*android:style/Theme.Material.Light.Dialog.Alert"
            android:priority="1000"
            android:process=":ui">
        </activity>

        <activity android:name=".ui.TelecomDeveloperMenu"
                android:label="@string/developer_title"
                android:exported="false"
+13 −1
Original line number Diff line number Diff line
@@ -111,7 +111,7 @@
    <!-- Button label on the "Missing voicemail number" dialog -->
    <string name="add_vm_number_str">Add number</string>

    <!-- Title of dialog used to comfirm whether the user intends to change the default dialer
    <!-- Title of dialog used to confirm whether the user intends to change the default dialer
            application [CHAR LIMIT=55]-->
    <string name="change_default_dialer_dialog_title">Make <xliff:g id="new_app">%s</xliff:g> your default Phone app?</string>
    <!-- Confirmation text that a user taps on to change the Default Phone App-->
@@ -121,6 +121,18 @@
    <!-- Warning message indicating what may happen if a user allows a 3rd party app to become the default dialer.-->
    <string name="change_default_dialer_warning_message"><xliff:g id="new_app">%s</xliff:g> will be able to place and control all aspects of calls. Only apps you trust should be set as the default Phone app.</string>

    <!-- Title of dialog used to confirm whether the user intends to change the default call screening
            application [CHAR LIMIT=55]-->
    <string name="change_default_call_screening_dialog_title">Make <xliff:g id="new_app">%s</xliff:g> your default call screening app?</string>
    <!-- Warning message indicating the old call screening app will no longer be able to screen calls.-->
    <string name="change_default_call_screening_warning_message_for_disable_old_app"><xliff:g id="old_app">%s</xliff:g> will no longer be able to screen calls.</string>
    <!-- Warning message indicating what may happen if a user allows a 3rd party app to become the default call screening.-->
    <string name="change_default_call_screening_warning_message"><xliff:g id="new_app">%s</xliff:g> will be able to see information about callers not in your contacts and will be able to block these calls. Only apps you trust should be set as the default call screening app.</string>
    <!-- Confirmation text that a user taps on to change the Default call screening App-->
    <string name="change_default_call_screening_dialog_affirmative">Set Default</string>
    <!-- Cancel text that a user taps on to not change the Default call screening App-->
    <string name="change_default_call_screening_dialog_negative">Cancel</string>

    <!-- Blocked numbers -->
    <string name="blocked_numbers">Blocked numbers</string>
    <!-- Text shown at the beginning of the blocked numbers screen to explain what the screen is about. -->
+190 −0
Original line number Diff line number Diff line
@@ -29,16 +29,21 @@ import android.Manifest;
import android.app.ActivityManager;
import android.app.AppOpsManager;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.res.Resources;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
import android.os.Process;
import android.os.UserHandle;
import android.provider.Settings;
import android.telecom.CallScreeningService;
import android.telecom.Log;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
@@ -47,10 +52,12 @@ import android.telecom.TelecomManager;
import android.telecom.VideoProfile;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.EventLog;

import com.android.internal.telecom.ITelecomService;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.telecom.components.ChangeDefaultCallScreeningApp;
import com.android.server.telecom.components.UserCallIntentProcessorFactory;
import com.android.server.telecom.settings.BlockedNumbersActivity;

@@ -77,6 +84,25 @@ public class TelecomServiceImpl {
        }
    }

    public interface SettingsSecureAdapter {
        void putStringForUser(ContentResolver resolver, String name, String value, int userHandle);

        String getStringForUser(ContentResolver resolver, String name, int userHandle);
    }

    static class SettingsSecureAdapterImpl implements SettingsSecureAdapter {
        @Override
        public void putStringForUser(ContentResolver resolver, String name, String value,
            int userHandle) {
            Settings.Secure.putStringForUser(resolver, name, value, userHandle);
        }

        @Override
        public String getStringForUser(ContentResolver resolver, String name, int userHandle) {
            return Settings.Secure.getStringForUser(resolver, name, userHandle);
        }
    }

    private static final String TIME_LINE_ARG = "timeline";
    private static final int DEFAULT_VIDEO_STATE = -1;
    private static final String PERMISSION_HANDLE_CALL_INTENT =
@@ -1319,6 +1345,147 @@ public class TelecomServiceImpl {
            }
        }

        /**
         * @see android.telecom.TelecomManager#requestChangeDefaultCallScreeningApp
         */
        @Override
        public void requestChangeDefaultCallScreeningApp(ComponentName componentName, String
            callingPackage) {
            try {
                Log.startSession("TSI.rCDCSA");
                synchronized (mLock) {
                    long token = Binder.clearCallingIdentity();
                    try {
                        if (callingPackage.equals(componentName.getPackageName())) {
                            final Intent intent = new Intent(mContext,
                                ChangeDefaultCallScreeningApp.class);
                            intent.putExtra(
                                TelecomManager.EXTRA_DEFAULT_CALL_SCREENING_APP_COMPONENT_NAME,
                                componentName.flattenToString());
                            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                            mContext.startActivity(intent);
                        } else {
                            throw new SecurityException(
                                "calling package name does't match the package of the passed "
                                    + "component name.");
                        }
                    } finally {
                        Binder.restoreCallingIdentity(token);
                    }
                }
            } finally {
                Log.endSession();
            }
        }

        /**
         * @see android.telecom.TelecomManager#isDefaultCallScreeningApp
         */
        @Override
        public boolean isDefaultCallScreeningApp(ComponentName componentName) {
            try {
                Log.startSession("TSI.iDCSA");
                synchronized (mLock) {
                    if (componentName == null) {
                        return false;
                    }
                    mAppOpsManager
                        .checkPackage(Binder.getCallingUid(), componentName.getPackageName());

                    long token = Binder.clearCallingIdentity();
                    try {
                        final String defaultPackage = mSettingsSecureAdapter
                            .getStringForUser(mContext.getContentResolver(),
                                Settings.Secure.CALL_SCREENING_DEFAULT_COMPONENT,
                                UserHandle.USER_CURRENT);

                        if (!TextUtils.isEmpty(defaultPackage) && !TextUtils
                            .isEmpty(componentName.flattenToString()) && TextUtils
                            .equals(defaultPackage, componentName.flattenToString())) {
                            return true;
                        } else {
                            Log.d(this,
                                "Provided package name is not the current default Call Screening application.");
                            return false;
                        }
                    } finally {
                        Binder.restoreCallingIdentity(token);
                    }
                }
            } finally {
                Log.endSession();
            }
        }

        /**
         * @see android.telecom.TelecomManager#setDefaultCallScreeningApp
         */
        @Override
        public void setDefaultCallScreeningApp(ComponentName componentName) {
            try {
                Log.startSession("TSI.sDCSA");
                enforcePermission(MODIFY_PHONE_STATE);
                enforcePermission(WRITE_SECURE_SETTINGS);
                synchronized (mLock) {
                    long token = Binder.clearCallingIdentity();
                    try {
                        try {
                            mContext.getPackageManager().getApplicationInfo(
                                componentName.getPackageName(), 0);
                        } catch (PackageManager.NameNotFoundException e) {
                            throw new IllegalArgumentException(
                                "the specified package name does't exist componentName = " +
                                    componentName);
                        }

                        Intent intent = new Intent(CallScreeningService.SERVICE_INTERFACE)
                            .setPackage(componentName.getPackageName());
                        List<ResolveInfo> entries = mContext.getPackageManager()
                            .queryIntentServicesAsUser(intent, 0,
                                mCallsManager.getCurrentUserHandle().getIdentifier());
                        if (entries.isEmpty()) {
                            throw new IllegalArgumentException(
                                "The specified package name doesn't have call screening services");
                        }

                        try {
                            ServiceInfo serviceInfo = mContext.getPackageManager().getServiceInfo(
                                componentName, 0);
                            if (!Manifest.permission.BIND_SCREENING_SERVICE.equals(serviceInfo
                                .permission)) {
                                throw new IllegalArgumentException(
                                    "The passed component doesn't require " +
                                        "BIND_SCREENING_SERVICE permission");
                            }

                        } catch (PackageManager.NameNotFoundException e) {
                            throw new IllegalArgumentException(
                                "the specified component name does't exist componentName = "
                                    + componentName);
                        }

                        final String oldComponentName = mSettingsSecureAdapter
                            .getStringForUser(mContext.getContentResolver(),
                                Settings.Secure.CALL_SCREENING_DEFAULT_COMPONENT,
                                UserHandle.USER_CURRENT);

                        broadcastCallScreeningAppChangedIntent(oldComponentName, false);

                        mSettingsSecureAdapter.putStringForUser(mContext.getContentResolver(),
                            Settings.Secure.CALL_SCREENING_DEFAULT_COMPONENT,
                            componentName.flattenToString(), UserHandle.USER_CURRENT);

                        broadcastCallScreeningAppChangedIntent(componentName.flattenToString(),
                            true);
                    } finally {
                        Binder.restoreCallingIdentity(token);
                    }
                }
            } finally {
                Log.endSession();
            }
        }

        @Override
        public TelecomAnalytics dumpCallAnalytics() {
            try {
@@ -1549,6 +1716,7 @@ public class TelecomServiceImpl {
    private final UserCallIntentProcessorFactory mUserCallIntentProcessorFactory;
    private final DefaultDialerCache mDefaultDialerCache;
    private final SubscriptionManagerAdapter mSubscriptionManagerAdapter;
    private final SettingsSecureAdapter mSettingsSecureAdapter;
    private final TelecomSystem.SyncRoot mLock;

    public TelecomServiceImpl(
@@ -1559,6 +1727,7 @@ public class TelecomServiceImpl {
            UserCallIntentProcessorFactory userCallIntentProcessorFactory,
            DefaultDialerCache defaultDialerCache,
            SubscriptionManagerAdapter subscriptionManagerAdapter,
            SettingsSecureAdapter settingsSecureAdapter,
            TelecomSystem.SyncRoot lock) {
        mContext = context;
        mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
@@ -1572,6 +1741,7 @@ public class TelecomServiceImpl {
        mDefaultDialerCache = defaultDialerCache;
        mCallIntentProcessorAdapter = callIntentProcessorAdapter;
        mSubscriptionManagerAdapter = subscriptionManagerAdapter;
        mSettingsSecureAdapter = settingsSecureAdapter;
    }

    public ITelecomService.Stub getBinder() {
@@ -1848,4 +2018,24 @@ public class TelecomServiceImpl {
        // If only TX or RX were set (or neither), the video state is valid.
        return remainingState == 0;
    }

    private void broadcastCallScreeningAppChangedIntent(String componentName,
        boolean isDefault) {
        if (TextUtils.isEmpty(componentName)) {
            return;
        }

        ComponentName broadcastComponentName = ComponentName.unflattenFromString(componentName);

        if (broadcastComponentName != null) {
            Intent intent = new Intent(TelecomManager
                .ACTION_DEFAULT_CALL_SCREENING_APP_CHANGED);
            intent.putExtra(TelecomManager
                .EXTRA_IS_DEFAULT_CALL_SCREENING_APP, isDefault);
            intent.putExtra(TelecomManager
                .EXTRA_DEFAULT_CALL_SCREENING_APP_COMPONENT_NAME, componentName);
            intent.setPackage(broadcastComponentName.getPackageName());
            mContext.sendBroadcast(intent);
        }
    }
}
+1 −0
Original line number Diff line number Diff line
@@ -332,6 +332,7 @@ public class TelecomSystem {
                },
                defaultDialerCache,
                new TelecomServiceImpl.SubscriptionManagerAdapterImpl(),
                new TelecomServiceImpl.SettingsSecureAdapterImpl(),
                mLock);
        Log.endSession();
    }
+141 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 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.telecom.components;

import com.android.internal.app.AlertActivity;
import com.android.internal.app.AlertController;
import com.android.server.telecom.R;

import android.content.ComponentName;
import android.content.DialogInterface;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.UserHandle;
import android.provider.Settings;
import android.telecom.Log;
import android.telecom.TelecomManager;
import android.text.TextUtils;

public class ChangeDefaultCallScreeningApp extends AlertActivity implements
        DialogInterface.OnClickListener {

    private static final String TAG = ChangeDefaultCallScreeningApp.class.getSimpleName();
    private ComponentName mNewComponentName;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mNewComponentName = ComponentName.unflattenFromString(getIntent().getStringExtra(
            TelecomManager.EXTRA_DEFAULT_CALL_SCREENING_APP_COMPONENT_NAME));

        if (canChangeDefaultCallScreening()) {
            buildDialog();
        } else {
            finish();
        }
    }

    @Override
    public void onClick(DialogInterface dialog, int which) {
        switch (which) {
            case BUTTON_POSITIVE:
                try {
                    TelecomManager.from(this).setDefaultCallScreeningApp(mNewComponentName);
                } catch (IllegalArgumentException e) {
                    Log.e(TAG, e, e.getMessage());
                    break;
                }
                break;
            case BUTTON_NEGATIVE:
                break;
        }
    }

    private boolean canChangeDefaultCallScreening() {
        boolean canChange;
        String defaultComponentName = Settings.Secure
            .getStringForUser(getApplicationContext().getContentResolver(),
                Settings.Secure.CALL_SCREENING_DEFAULT_COMPONENT, UserHandle.USER_CURRENT);

        ComponentName oldComponentName = TextUtils.isEmpty(defaultComponentName) ? null
            : ComponentName.unflattenFromString(defaultComponentName);

        canChange = oldComponentName == null || !oldComponentName.getPackageName()
            .equals(mNewComponentName.getPackageName()) || !oldComponentName.flattenToString()
            .equals(mNewComponentName.flattenToString());

        return canChange;
    }

    private void buildDialog() {
        final String newPackageLabel = getApplicationLabel(mNewComponentName);
        final AlertController.AlertParams p = mAlertParams;

        p.mTitle = getString(R.string.change_default_call_screening_dialog_title, newPackageLabel);
        p.mMessage = getDialogBody(newPackageLabel);
        p.mPositiveButtonText = getString(
            R.string.change_default_call_screening_dialog_affirmative);
        p.mNegativeButtonText = getString(R.string.change_default_call_screening_dialog_negative);
        p.mPositiveButtonListener = this;
        p.mNegativeButtonListener = this;

        setupAlert();
    }

    private String getDialogBody(String newPackageLabel) {
        final String oldPackage = Settings.Secure.getStringForUser(
            getApplicationContext().getContentResolver(),
            Settings.Secure.CALL_SCREENING_DEFAULT_COMPONENT, UserHandle.USER_CURRENT);
        ComponentName oldPackageComponentName = null;
        if (!TextUtils.isEmpty(oldPackage)) {
            oldPackageComponentName = ComponentName.unflattenFromString(oldPackage);
        }

        String dialogBody = getString(R.string.change_default_call_screening_warning_message,
            newPackageLabel);

        if (oldPackageComponentName != null) {
            dialogBody = getString(
                R.string.change_default_call_screening_warning_message_for_disable_old_app,
                getApplicationLabel(oldPackageComponentName)) + " "
                + dialogBody;
        }

        return dialogBody;
    }

    /**
     * Returns the application label that corresponds to the given package name
     *
     * @return Application label for the given package name, or null if not found.
     */
    private String getApplicationLabel(ComponentName componentName) {
        final PackageManager pm = getPackageManager();

        try {
            ApplicationInfo info = pm.getApplicationInfo(componentName.getPackageName(), 0);
            return pm.getApplicationLabel(info).toString();
        } catch (PackageManager.NameNotFoundException e) {
            Log.w(TAG, "Application info not found for packageName " + componentName
                .getPackageName());
        }

        return componentName.getPackageName();
    }
}
Loading