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

Commit 8cdae012 authored by Annie Meng's avatar Annie Meng
Browse files

Clear calling identity in BMS.backupNow

BMS.backupNow is called from GMSCore, which has a different calling
identity than the framework. This causes an IllegalArgumentException due
to uid mismatch when backupNow tries to schedule a job.

This occurs when the following conditions are combined:
1) Battery saver mode enabled
2) Network change detected
3) Backup pass is scheduled for now

Bug: 79441902
Test: 1) m -j RunFrameworksServicesRoboTests
2) Manual: Enabled battery saver, modified code to run backupNow with
each network change and overwrite previously scheduled KeyValueJob.
Then, change wifi to trigger scheduling the KeyValueJob.
Verified:
- IllegalStateException b/c of uid mismatch without change
- No exception and correct calling uid with change

Change-Id: Iac90cd435e3fc32ff5428236aa15507b36aa833d
parent f4c19894
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -32,7 +32,7 @@ import com.android.internal.annotations.VisibleForTesting;
 * <p>The backup manager constants are encoded as a key value list separated by commas and stored as
 * a Settings.Secure.
 */
class BackupManagerConstants extends KeyValueSettingObserver {
public class BackupManagerConstants extends KeyValueSettingObserver {
    private static final String TAG = "BackupManagerConstants";
    private static final String SETTING = Settings.Secure.BACKUP_MANAGER_CONSTANTS;

+27 −17
Original line number Diff line number Diff line
@@ -353,6 +353,11 @@ public class BackupManagerService implements BackupManagerServiceInterface {
        mAlarmManager = alarmManager;
    }

    @VisibleForTesting
    void setPowerManager(PowerManager powerManager) {
        mPowerManager = powerManager;
    }

    public void setBackupManagerBinder(IBackupManager backupManagerBinder) {
        mBackupManagerBinder = backupManagerBinder;
    }
@@ -2410,6 +2415,8 @@ public class BackupManagerService implements BackupManagerServiceInterface {
    public void backupNow() {
        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "backupNow");

        long oldId = Binder.clearCallingIdentity();
        try {
            final PowerSaveState result =
                    mPowerManager.getPowerSaveState(ServiceType.KEYVALUE_BACKUP);
            if (result.batterySaverEnabled) {
@@ -2430,6 +2437,9 @@ public class BackupManagerService implements BackupManagerServiceInterface {
                    KeyValueBackupJob.cancel(mContext);
                }
            }
        } finally {
            Binder.restoreCallingIdentity(oldId);
        }
    }

    public boolean deviceIsProvisioned() {
+54 −7
Original line number Diff line number Diff line
@@ -22,9 +22,7 @@ import static com.android.server.backup.testing.TransportData.d2dTransport;
import static com.android.server.backup.testing.TransportData.localTransport;
import static com.android.server.backup.testing.TransportTestUtils.setUpCurrentTransport;
import static com.android.server.backup.testing.TransportTestUtils.setUpTransports;

import static com.google.common.truth.Truth.assertThat;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
@@ -43,9 +41,10 @@ import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
import android.os.HandlerThread;
import android.os.PowerManager;
import android.os.PowerSaveState;
import android.platform.test.annotations.Presubmit;
import android.provider.Settings;

import com.android.server.backup.internal.BackupRequest;
import com.android.server.backup.testing.TransportData;
import com.android.server.backup.testing.TransportTestUtils.TransportMock;
@@ -54,8 +53,11 @@ import com.android.server.testing.FrameworkRobolectricTestRunner;
import com.android.server.testing.SystemLoaderPackages;
import com.android.server.testing.shadows.ShadowAppBackupUtils;
import com.android.server.testing.shadows.ShadowBackupPolicyEnforcer;
import com.android.server.testing.shadows.ShadowBinder;
import com.android.server.testing.shadows.ShadowKeyValueBackupJob;
import com.android.server.testing.shadows.ShadowPerformBackupTask;

import java.io.File;
import java.util.List;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -64,6 +66,7 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.annotation.Implements;
import org.robolectric.shadows.ShadowContextWrapper;
import org.robolectric.shadows.ShadowLog;
import org.robolectric.shadows.ShadowLooper;
@@ -71,9 +74,6 @@ import org.robolectric.shadows.ShadowPackageManager;
import org.robolectric.shadows.ShadowSettings;
import org.robolectric.shadows.ShadowSystemClock;

import java.io.File;
import java.util.List;

@RunWith(FrameworkRobolectricTestRunner.class)
@Config(
        manifest = Config.NONE,
@@ -796,6 +796,34 @@ public class BackupManagerServiceTest {
        tearDownForRequestBackup();
    }

    @Test
    @Config(shadows = {ShadowBinder.class, ShadowKeyValueBackupJob.class})
    public void testBackupNow_clearsCallingIdentityForJobScheduler() {
        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
        BackupManagerService backupManagerService = createInitializedBackupManagerService();
        setUpPowerManager(backupManagerService);
        ShadowBinder.setCallingUid(1);

        backupManagerService.backupNow();

        assertThat(ShadowKeyValueBackupJob.getCallingUid()).isEqualTo(ShadowBinder.LOCAL_UID);
        assertThat(ShadowBinder.getCallingUid()).isEqualTo(1);
    }

    @Test
    @Config(shadows = {ShadowBinder.class, ShadowKeyValueBackupJobException.class})
    public void testBackupNow_whenExceptionThrown_restoresCallingIdentity() {
        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
        BackupManagerService backupManagerService = createInitializedBackupManagerService();
        setUpPowerManager(backupManagerService);
        ShadowBinder.setCallingUid(1);

        expectThrows(IllegalArgumentException.class, backupManagerService::backupNow);
        assertThat(ShadowKeyValueBackupJobException.getCallingUid())
                .isEqualTo(ShadowBinder.LOCAL_UID);
        assertThat(ShadowBinder.getCallingUid()).isEqualTo(1);
    }

    private BackupManagerService createBackupManagerServiceForRequestBackup() {
        BackupManagerService backupManagerService = createInitializedBackupManagerService();
        backupManagerService.setEnabled(true);
@@ -853,4 +881,23 @@ public class BackupManagerServiceTest {
        ShadowSystemClock.setCurrentTimeMillis(mShadowBackupLooper.getScheduler().getCurrentTime());
        return backupManagerService;
    }

    private void setUpPowerManager(BackupManagerService backupManagerService) {
        PowerManager powerManagerMock = mock(PowerManager.class);
        when(powerManagerMock.getPowerSaveState(anyInt()))
                .thenReturn(new PowerSaveState.Builder().setBatterySaverEnabled(true).build());
        backupManagerService.setPowerManager(powerManagerMock);
    }

    /**
     * We can't mock the void method {@link #schedule(Context, long, BackupManagerConstants)} so we
     * extend {@link ShadowKeyValueBackupJob} and throw an exception at the end of the method.
     */
    @Implements(KeyValueBackupJob.class)
    public static class ShadowKeyValueBackupJobException extends ShadowKeyValueBackupJob {
        public static void schedule(Context ctx, long delay, BackupManagerConstants constants) {
            ShadowKeyValueBackupJob.schedule(ctx, delay, constants);
            throw new IllegalArgumentException();
        }
    }
}
+44 −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.testing.shadows;

import android.os.Binder;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;

/**
 * Extends {@link org.robolectric.shadows.ShadowBinder} with {@link Binder#clearCallingIdentity()}
 * and {@link Binder#restoreCallingIdentity(long)}. Uses a hardcoded default {@link #LOCAL_UID} to
 * mimic the local process uid.
 */
@Implements(Binder.class)
public class ShadowBinder extends org.robolectric.shadows.ShadowBinder {
    public static final Integer LOCAL_UID = 1000;
    private static Integer originalCallingUid;

    @Implementation
    public static long clearCallingIdentity() {
        originalCallingUid = getCallingUid();
        setCallingUid(LOCAL_UID);
        return 1L;
    }

    @Implementation
    public static void restoreCallingIdentity(long token) {
        setCallingUid(originalCallingUid);
    }
}
+38 −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.testing.shadows;

import android.content.Context;
import android.os.Binder;
import com.android.server.backup.BackupManagerConstants;
import com.android.server.backup.KeyValueBackupJob;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;

@Implements(KeyValueBackupJob.class)
public class ShadowKeyValueBackupJob {
    private static int callingUid;

    public static int getCallingUid() {
        return callingUid;
    }

    @Implementation
    public static void schedule(Context ctx, long delay, BackupManagerConstants constants) {
        callingUid = Binder.getCallingUid();
    }
}