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

Commit f8b3a82b authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Correctly support restoring SIM specific settings"

parents 2932295d ca2f8f26
Loading
Loading
Loading
Loading
+34 −0
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import static android.telephony.UiccSlotInfo.CARD_STATE_INFO_PRESENT;
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.app.AppOpsManager;
import android.app.PendingIntent;
import android.app.compat.CompatChanges;
@@ -43,6 +44,7 @@ import android.database.Cursor;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.ParcelUuid;
import android.os.PersistableBundle;
@@ -4965,6 +4967,38 @@ public class SubscriptionController extends ISub.Stub {
        return false;
    }

    /**
     * Called during setup wizard restore flow to attempt to restore the backed up sim-specific
     * configs to device for all existing SIMs in the subscription database {@link SimInfo}.
     * Internally, it will store the backup data in an internal file. This file will persist on
     * device for device's lifetime and will be used later on when a SIM is inserted to restore that
     * specific SIM's settings. End result is subscription database is modified to match any backed
     * up configs for the appropriate inserted SIMs.
     *
     * <p>
     * The {@link Uri} {@link SubscriptionManager#SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI} is
     * notified if any {@link SimInfo} entry is updated as the result of this method call.
     *
     * @param data with the sim specific configs to be backed up.
     */
    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
    @Override
    public void restoreAllSimSpecificSettingsFromBackup(@NonNull byte[] data) {
        enforceModifyPhoneState("restoreAllSimSpecificSettingsFromBackup");

        long token = Binder.clearCallingIdentity();
        try {
            Bundle bundle = new Bundle();
            bundle.putByteArray(SubscriptionManager.KEY_SIM_SPECIFIC_SETTINGS_DATA, data);
            mContext.getContentResolver().call(
                    SubscriptionManager.SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI,
                    SubscriptionManager.RESTORE_SIM_SPECIFIC_SETTINGS_METHOD_NAME,
                    null, bundle);
        } finally {
            Binder.restoreCallingIdentity(token);
        }
    }

    /**
     * @hide
     */
+4 −2
Original line number Diff line number Diff line
@@ -717,8 +717,10 @@ public class SubscriptionInfoUpdater extends Handler {
    }

    private void restoreSimSpecificSettingsForPhone(int phoneId) {
        SubscriptionManager subManager = SubscriptionManager.from(sContext);
        subManager.restoreSimSpecificSettingsForIccIdFromBackup(sIccId[phoneId]);
        sContext.getContentResolver().call(
                SubscriptionManager.SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI,
                SubscriptionManager.RESTORE_SIM_SPECIFIC_SETTINGS_METHOD_NAME,
                sIccId[phoneId], null);
    }

    private void updateCarrierServices(int phoneId, String simState) {
+72 −37
Original line number Diff line number Diff line
@@ -505,8 +505,9 @@ public class SubscriptionDatabaseManager extends Handler {
    private final Map<Integer, SubscriptionInfoInternal> mAllSubscriptionInfoInternalCache =
            new HashMap<>(16);

    /** Whether database has been loaded into the cache after boot up. */
    private boolean mDatabaseLoaded = false;
    /** Whether database has been initialized after boot up. */
    @GuardedBy("mDatabaseInitialized")
    private Boolean mDatabaseInitialized = false;

    /**
     * This is the callback used for listening events from {@link SubscriptionDatabaseManager}.
@@ -542,9 +543,9 @@ public class SubscriptionDatabaseManager extends Handler {
        }

        /**
         * Called when database has been loaded into the cache.
         * Called when database has been initialized.
         */
        public abstract void onDatabaseLoaded();
        public abstract void onInitialized();

        /**
         * Called when subscription changed.
@@ -578,7 +579,7 @@ public class SubscriptionDatabaseManager extends Handler {
        mUiccController = UiccController.getInstance();
        mAsyncMode = mContext.getResources().getBoolean(
                com.android.internal.R.bool.config_subscription_database_async_update);
        loadFromDatabase();
        initializeDatabase();
    }

    /**
@@ -757,10 +758,13 @@ public class SubscriptionDatabaseManager extends Handler {
                    + "insert. subInfo=" + subInfo);
        }

        if (!mDatabaseLoaded) {
            throw new IllegalStateException("Database has not been loaded. Can't insert new "
        synchronized (mDatabaseInitialized) {
            if (!mDatabaseInitialized) {
                throw new IllegalStateException(
                        "Database has not been initialized. Can't insert new "
                                + "record at this point.");
            }
        }

        int subId;
        // Grab the write lock so no other threads can read or write the cache.
@@ -826,11 +830,13 @@ public class SubscriptionDatabaseManager extends Handler {
    private int updateDatabase(int subId, @NonNull ContentValues contentValues) {
        logv("updateDatabase: prepare to update sub " + subId);

        if (!mDatabaseLoaded) {
            logel("updateDatabase: Database has not been loaded. Can't update database at this "
                    + "point. contentValues=" + contentValues);
        synchronized (mDatabaseInitialized) {
            if (!mDatabaseInitialized) {
                logel("updateDatabase: Database has not been initialized. Can't update database at "
                        + "this point. contentValues=" + contentValues);
                return 0;
            }
        }

        if (mAsyncMode) {
            // Perform the update in the handler thread asynchronously.
@@ -1794,39 +1800,68 @@ public class SubscriptionDatabaseManager extends Handler {
    }

    /**
     * Load the entire database into the cache.
     * Reload the database from content provider to the cache.
     */
    private void loadFromDatabase() {
        // Perform the task in the handler thread.
        Runnable r = () -> {
    public void reloadDatabase() {
        if (mAsyncMode) {
            post(this::loadDatabaseInternal);
        } else {
            loadDatabaseInternal();
        }
    }

    /**
     * Load the database from content provider to the cache.
     */
    private void loadDatabaseInternal() {
        logl("loadDatabaseInternal");
        try (Cursor cursor = mContext.getContentResolver().query(
                SimInfo.CONTENT_URI, null, null, null, null)) {
            mReadWriteLock.writeLock().lock();
            try {
                    mAllSubscriptionInfoInternalCache.clear();
                Map<Integer, SubscriptionInfoInternal> newAllSubscriptionInfoInternalCache =
                        new HashMap<>();
                while (cursor != null && cursor.moveToNext()) {
                    SubscriptionInfoInternal subInfo = createSubscriptionInfoFromCursor(cursor);
                        if (subInfo != null) {
                            mAllSubscriptionInfoInternalCache
                                    .put(subInfo.getSubscriptionId(), subInfo);
                    newAllSubscriptionInfoInternalCache.put(subInfo.getSubscriptionId(), subInfo);
                    if (!Objects.equals(mAllSubscriptionInfoInternalCache
                            .get(subInfo.getSubscriptionId()), subInfo)) {
                        mCallback.invokeFromExecutor(() -> mCallback.onSubscriptionChanged(
                                subInfo.getSubscriptionId()));
                    }
                }
                    mDatabaseLoaded = true;
                    mCallback.invokeFromExecutor(mCallback::onDatabaseLoaded);
                    log("Loaded " + mAllSubscriptionInfoInternalCache.size()

                mAllSubscriptionInfoInternalCache.clear();
                mAllSubscriptionInfoInternalCache.putAll(newAllSubscriptionInfoInternalCache);

                logl("Loaded " + mAllSubscriptionInfoInternalCache.size()
                        + " records from the subscription database.");
            } finally {
                mReadWriteLock.writeLock().unlock();
            }
        }
        };
    }

    /**
     * Initialize the database cache. Load the entire database into the cache.
     */
    private void initializeDatabase() {
        if (mAsyncMode) {
            // Load the database asynchronously.
            post(r);
            post(() -> {
                synchronized (mDatabaseInitialized) {
                    loadDatabaseInternal();
                    mDatabaseInitialized = true;
                    mCallback.invokeFromExecutor(mCallback::onInitialized);
                }
            });
        } else {
            // Load the database synchronously.
            r.run();
            synchronized (mDatabaseInitialized) {
                loadDatabaseInternal();
                mDatabaseInitialized = true;
                mCallback.invokeFromExecutor(mCallback::onInitialized);
            }
        }
    }

@@ -1837,7 +1872,7 @@ public class SubscriptionDatabaseManager extends Handler {
     *
     * @return The subscription info from a single database record.
     */
    @Nullable
    @NonNull
    private SubscriptionInfoInternal createSubscriptionInfoFromCursor(@NonNull Cursor cursor) {
        SubscriptionInfoInternal.Builder builder = new SubscriptionInfoInternal.Builder();
        int id = cursor.getInt(cursor.getColumnIndexOrThrow(
+54 −6
Original line number Diff line number Diff line
@@ -33,13 +33,16 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.ParcelUuid;
import android.os.PersistableBundle;
import android.os.Process;
import android.os.RemoteException;
import android.os.TelephonyServiceManager;
import android.os.UserHandle;
@@ -221,7 +224,7 @@ public class SubscriptionManagerService extends ISub.Stub {

    /** Local log for most important debug messages. */
    @NonNull
    private final LocalLog mLocalLog = new LocalLog(128);
    private final LocalLog mLocalLog = new LocalLog(256);

    /** The subscription database manager. */
    @NonNull
@@ -476,8 +479,8 @@ public class SubscriptionManagerService extends ISub.Stub {
                     * Called when database has been loaded into the cache.
                     */
                    @Override
                    public void onDatabaseLoaded() {
                        log("Subscription database has been loaded.");
                    public void onInitialized() {
                        log("Subscription database has been initialized.");
                        for (int phoneId = 0; phoneId < mTelephonyManager.getActiveModemCount()
                                ; phoneId++) {
                            markSubscriptionsInactive(phoneId);
@@ -557,6 +560,7 @@ public class SubscriptionManagerService extends ISub.Stub {

        SubscriptionManager.invalidateSubscriptionManagerServiceCaches();
        SubscriptionManager.invalidateSubscriptionManagerServiceEnabledCaches();
        logl("created");
    }

    /**
@@ -1335,7 +1339,11 @@ public class SubscriptionManagerService extends ISub.Stub {
                }

                // Attempt to restore SIM specific settings when SIM is loaded.
                mSubscriptionManager.restoreSimSpecificSettingsForIccIdFromBackup(iccId);
                mContext.getContentResolver().call(
                        SubscriptionManager.SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI,
                        SubscriptionManager.RESTORE_SIM_SPECIFIC_SETTINGS_METHOD_NAME,
                        iccId, null);
                mSubscriptionDatabaseManager.reloadDatabase();
            }
        } else {
            log("updateSubscriptions: No ICCID available for phone " + phoneId);
@@ -2854,7 +2862,7 @@ public class SubscriptionManagerService extends ISub.Stub {
        final long token = Binder.clearCallingIdentity();
        try {
            logl("setSubscriptionProperty: subId=" + subId + ", columnName=" + columnName
                    + ", value=" + value);
                    + ", value=" + value + ", calling package=" + getCallingPackage());

            if (!SimInfo.getAllColumns().contains(columnName)) {
                throw new IllegalArgumentException("Invalid column name " + columnName);
@@ -3486,7 +3494,7 @@ public class SubscriptionManagerService extends ISub.Stub {
     * @param subscriptionId the subId of the subscription
     * @param userHandle user handle of the user
     * @return {@code true} if subscription is associated with user
     * {code true} if there are no subscriptions on device
     * {@code true} if there are no subscriptions on device
     * else {@code false} if subscription is not associated with user.
     *
     * @throws SecurityException if the caller doesn't have permissions required.
@@ -3593,6 +3601,42 @@ public class SubscriptionManagerService extends ISub.Stub {
        return true;
    }

    /**
     * Called during setup wizard restore flow to attempt to restore the backed up sim-specific
     * configs to device for all existing SIMs in the subscription database {@link SimInfo}.
     * Internally, it will store the backup data in an internal file. This file will persist on
     * device for device's lifetime and will be used later on when a SIM is inserted to restore that
     * specific SIM's settings. End result is subscription database is modified to match any backed
     * up configs for the appropriate inserted SIMs.
     *
     * <p>
     * The {@link Uri} {@link SubscriptionManager#SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI} is
     * notified if any {@link SimInfo} entry is updated as the result of this method call.
     *
     * @param data with the sim specific configs to be backed up.
     */
    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
    @Override
    public void restoreAllSimSpecificSettingsFromBackup(@NonNull byte[] data) {
        enforcePermissions("restoreAllSimSpecificSettingsFromBackup",
                Manifest.permission.MODIFY_PHONE_STATE);

        long token = Binder.clearCallingIdentity();
        try {
            Bundle bundle = new Bundle();
            bundle.putByteArray(SubscriptionManager.KEY_SIM_SPECIFIC_SETTINGS_DATA, data);
            logl("restoreAllSimSpecificSettingsFromBackup");
            mContext.getContentResolver().call(
                    SubscriptionManager.SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI,
                    SubscriptionManager.RESTORE_SIM_SPECIFIC_SETTINGS_METHOD_NAME,
                    null, bundle);
            // After restoring, we need to reload the content provider into the cache.
            mSubscriptionDatabaseManager.reloadDatabase();
        } finally {
            Binder.restoreCallingIdentity(token);
        }
    }

    /**
     * Register the callback for receiving information from {@link SubscriptionManagerService}.
     *
@@ -3737,6 +3781,10 @@ public class SubscriptionManagerService extends ISub.Stub {
     */
    @NonNull
    private String getCallingPackage() {
        if (Binder.getCallingUid() == Process.PHONE_UID) {
            // Too many packages running with phone uid. Just return one here.
            return "com.android.phone";
        }
        return Arrays.toString(mContext.getPackageManager().getPackagesForUid(
                Binder.getCallingUid()));
    }
+7 −0
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.annotation.Nullable;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.Context;
@@ -40,6 +41,7 @@ import android.content.Intent;
import android.content.pm.IPackageManager;
import android.content.pm.UserInfo;
import android.net.Uri;
import android.os.Bundle;
import android.os.Looper;
import android.os.ParcelUuid;
import android.os.PersistableBundle;
@@ -107,6 +109,11 @@ public class SubscriptionInfoUpdaterTest extends TelephonyTest {
        public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
            return mContentProvider.update(uri, values, selection, selectionArgs);
        }

        @Override
        public Bundle call(String method, @Nullable String args, @Nullable Bundle bundle) {
            return new Bundle();
        }
    }

    @Before
Loading