Grant DCS storage access; better OBB errors.

DCS had been relying on the WRITE_MEDIA_STORAGE permission to access
OBBs on external storage, but that permission has been locked down,
and we need to use the real WRITE_EXTERNAL_STORAGE permission now.

Rework the OBB error reporting flow to bubble exact error codes up
from internals, so that we can return expected CTS error codes.

Test: cts-tradefed run commandAndExit cts-dev -m CtsOsTestCases -t android.os.storage.cts.StorageManagerTest
Bug: 73424392
Change-Id: Iecbc4132745d56ebf081868ad2f9c3efe1e3735f
This commit is contained in:
Jeff Sharkey
2018-02-15 13:06:53 -07:00
parent 3270ae05d4
commit 0095a82b14
5 changed files with 83 additions and 173 deletions

View File

@@ -1,75 +0,0 @@
/*
* Copyright (C) 2007 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 android.os.storage;
/**
* Class that provides access to constants returned from StorageManager
* and lower level StorageManagerService APIs.
*
* @hide
*/
public class StorageResultCode
{
/**
* Operation succeeded.
* @see android.os.storage.StorageManager
*/
public static final int OperationSucceeded = 0;
/**
* Operation failed: Internal error.
* @see android.os.storage.StorageManager
*/
public static final int OperationFailedInternalError = -1;
/**
* Operation failed: Missing media.
* @see android.os.storage.StorageManager
*/
public static final int OperationFailedNoMedia = -2;
/**
* Operation failed: Media is blank.
* @see android.os.storage.StorageManager
*/
public static final int OperationFailedMediaBlank = -3;
/**
* Operation failed: Media is corrupt.
* @see android.os.storage.StorageManager
*/
public static final int OperationFailedMediaCorrupt = -4;
/**
* Operation failed: Storage not mounted.
* @see android.os.storage.StorageManager
*/
public static final int OperationFailedStorageNotMounted = -5;
/**
* Operation failed: Storage is mounted.
* @see android.os.storage.StorageManager
*/
public static final int OperationFailedStorageMounted = -6;
/**
* Operation failed: Storage is busy.
* @see android.os.storage.StorageManager
*/
public static final int OperationFailedStorageBusy = -7;
}

View File

@@ -6,9 +6,6 @@
<uses-permission android:name="android.permission.ASEC_DESTROY"/>
<uses-permission android:name="android.permission.ASEC_MOUNT_UNMOUNT"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!-- Used to improve MeasureUtils performance on emulated storage, and to
view storage for all users -->
<uses-permission android:name="android.permission.WRITE_MEDIA_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_CACHE_FILESYSTEM" />
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />

View File

@@ -16,6 +16,15 @@
package com.android.server;
import static android.os.storage.OnObbStateChangeListener.ERROR_ALREADY_MOUNTED;
import static android.os.storage.OnObbStateChangeListener.ERROR_COULD_NOT_MOUNT;
import static android.os.storage.OnObbStateChangeListener.ERROR_COULD_NOT_UNMOUNT;
import static android.os.storage.OnObbStateChangeListener.ERROR_INTERNAL;
import static android.os.storage.OnObbStateChangeListener.ERROR_NOT_MOUNTED;
import static android.os.storage.OnObbStateChangeListener.ERROR_PERMISSION_DENIED;
import static android.os.storage.OnObbStateChangeListener.MOUNTED;
import static android.os.storage.OnObbStateChangeListener.UNMOUNTED;
import static com.android.internal.util.XmlUtils.readBooleanAttribute;
import static com.android.internal.util.XmlUtils.readIntAttribute;
import static com.android.internal.util.XmlUtils.readLongAttribute;
@@ -60,7 +69,6 @@ import android.os.FileUtils;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.IProgressListener;
import android.os.IStoraged;
import android.os.IVold;
import android.os.IVoldListener;
@@ -87,7 +95,6 @@ import android.os.storage.IStorageShutdownObserver;
import android.os.storage.OnObbStateChangeListener;
import android.os.storage.StorageManager;
import android.os.storage.StorageManagerInternal;
import android.os.storage.StorageResultCode;
import android.os.storage.StorageVolume;
import android.os.storage.VolumeInfo;
import android.os.storage.VolumeRecord;
@@ -137,8 +144,7 @@ import java.io.IOException;
import java.io.PrintWriter;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.security.GeneralSecurityException;
import java.security.spec.KeySpec;
import java.util.ArrayList;
import java.util.Arrays;
@@ -3032,8 +3038,8 @@ class StorageManagerService extends IStorageManager.Stub
// If this is the only one pending we might
// have to bind to the service again.
if (!connectToService()) {
Slog.e(TAG, "Failed to bind to media container service");
action.handleError();
action.notifyObbStateChange(new ObbException(ERROR_INTERNAL,
"Failed to bind to media container service"));
return;
}
}
@@ -3049,10 +3055,10 @@ class StorageManagerService extends IStorageManager.Stub
}
if (mContainerService == null) {
// Something seriously wrong. Bail out
Slog.e(TAG, "Cannot bind to media container service");
for (ObbAction action : mActions) {
// Indicate service bind error
action.handleError();
action.notifyObbStateChange(new ObbException(ERROR_INTERNAL,
"Failed to bind to media container service"));
}
mActions.clear();
} else if (mActions.size() > 0) {
@@ -3074,10 +3080,10 @@ class StorageManagerService extends IStorageManager.Stub
disconnectService();
}
if (!connectToService()) {
Slog.e(TAG, "Failed to bind to media container service");
for (ObbAction action : mActions) {
// Indicate service bind error
action.handleError();
action.notifyObbStateChange(new ObbException(ERROR_INTERNAL,
"Failed to bind to media container service"));
}
mActions.clear();
}
@@ -3167,6 +3173,20 @@ class StorageManagerService extends IStorageManager.Stub
}
}
private static class ObbException extends Exception {
public final int status;
public ObbException(int status, String message) {
super(message);
this.status = status;
}
public ObbException(int status, Throwable cause) {
super(cause.getMessage(), cause);
this.status = status;
}
}
abstract class ObbAction {
private static final int MAX_RETRIES = 3;
private int mRetries;
@@ -3183,46 +3203,44 @@ class StorageManagerService extends IStorageManager.Stub
Slog.i(TAG, "Starting to execute action: " + toString());
mRetries++;
if (mRetries > MAX_RETRIES) {
Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up");
mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND);
handleError();
notifyObbStateChange(new ObbException(ERROR_INTERNAL,
"Failed to bind to media container service"));
} else {
handleExecute();
if (DEBUG_OBB)
Slog.i(TAG, "Posting install MCS_UNBIND");
mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND);
}
} catch (RemoteException e) {
if (DEBUG_OBB)
Slog.i(TAG, "Posting install MCS_RECONNECT");
mObbActionHandler.sendEmptyMessage(OBB_MCS_RECONNECT);
} catch (Exception e) {
if (DEBUG_OBB)
Slog.d(TAG, "Error handling OBB action", e);
handleError();
} catch (ObbException e) {
notifyObbStateChange(e);
mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND);
}
}
abstract void handleExecute() throws RemoteException, IOException;
abstract void handleError();
abstract void handleExecute() throws ObbException;
protected ObbInfo getObbInfo() throws IOException {
ObbInfo obbInfo;
protected ObbInfo getObbInfo() throws ObbException {
final ObbInfo obbInfo;
try {
obbInfo = mContainerService.getObbInfo(mObbState.canonicalPath);
} catch (RemoteException e) {
Slog.d(TAG, "Couldn't call DefaultContainerService to fetch OBB info for "
+ mObbState.canonicalPath);
obbInfo = null;
} catch (Exception e) {
throw new ObbException(ERROR_PERMISSION_DENIED, e);
}
if (obbInfo == null) {
throw new IOException("Couldn't read OBB file: " + mObbState.canonicalPath);
if (obbInfo != null) {
return obbInfo;
} else {
throw new ObbException(ERROR_INTERNAL,
"Missing OBB info for: " + mObbState.canonicalPath);
}
return obbInfo;
}
protected void sendNewStatusOrIgnore(int status) {
protected void notifyObbStateChange(ObbException e) {
Slog.w(TAG, e);
notifyObbStateChange(e.status);
}
protected void notifyObbStateChange(int status) {
if (mObbState == null || mObbState.token == null) {
return;
}
@@ -3246,16 +3264,14 @@ class StorageManagerService extends IStorageManager.Stub
}
@Override
public void handleExecute() throws IOException, RemoteException {
public void handleExecute() throws ObbException {
warnOnNotMounted();
final ObbInfo obbInfo = getObbInfo();
if (!isUidOwnerOfPackageOrSystem(obbInfo.packageName, mCallingUid)) {
Slog.w(TAG, "Denied attempt to mount OBB " + obbInfo.filename
+ " which is owned by " + obbInfo.packageName);
sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
return;
throw new ObbException(ERROR_PERMISSION_DENIED, "Denied attempt to mount OBB "
+ obbInfo.filename + " which is owned by " + obbInfo.packageName);
}
final boolean isMounted;
@@ -3263,9 +3279,8 @@ class StorageManagerService extends IStorageManager.Stub
isMounted = mObbPathToStateMap.containsKey(mObbState.rawPath);
}
if (isMounted) {
Slog.w(TAG, "Attempt to mount OBB which is already mounted: " + obbInfo.filename);
sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_ALREADY_MOUNTED);
return;
throw new ObbException(ERROR_ALREADY_MOUNTED,
"Attempt to mount OBB which is already mounted: " + obbInfo.filename);
}
final String hashedKey;
@@ -3283,28 +3298,16 @@ class StorageManagerService extends IStorageManager.Stub
BigInteger bi = new BigInteger(key.getEncoded());
hashedKey = bi.toString(16);
binderKey = hashedKey;
} catch (NoSuchAlgorithmException e) {
Slog.e(TAG, "Could not load PBKDF2 algorithm", e);
sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
return;
} catch (InvalidKeySpecException e) {
Slog.e(TAG, "Invalid key spec when loading PBKDF2 algorithm", e);
sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
return;
} catch (GeneralSecurityException e) {
throw new ObbException(ERROR_INTERNAL, e);
}
}
int rc = StorageResultCode.OperationSucceeded;
try {
mObbState.volId = mVold.createObb(mObbState.canonicalPath, binderKey,
mObbState.ownerGid);
mVold.mount(mObbState.volId, 0, -1);
} catch (Exception e) {
Slog.w(TAG, e);
rc = StorageResultCode.OperationFailedInternalError;
}
if (rc == StorageResultCode.OperationSucceeded) {
if (DEBUG_OBB)
Slog.d(TAG, "Successfully mounted OBB " + mObbState.canonicalPath);
@@ -3312,19 +3315,12 @@ class StorageManagerService extends IStorageManager.Stub
addObbStateLocked(mObbState);
}
sendNewStatusOrIgnore(OnObbStateChangeListener.MOUNTED);
} else {
Slog.e(TAG, "Couldn't mount OBB file: " + rc);
sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_COULD_NOT_MOUNT);
notifyObbStateChange(MOUNTED);
} catch (Exception e) {
throw new ObbException(ERROR_COULD_NOT_MOUNT, e);
}
}
@Override
public void handleError() {
sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
@@ -3344,7 +3340,7 @@ class StorageManagerService extends IStorageManager.Stub
}
@Override
public void handleExecute() throws IOException {
public void handleExecute() throws ObbException {
warnOnNotMounted();
final ObbState existingState;
@@ -3353,44 +3349,31 @@ class StorageManagerService extends IStorageManager.Stub
}
if (existingState == null) {
sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_NOT_MOUNTED);
return;
throw new ObbException(ERROR_NOT_MOUNTED, "Missing existingState");
}
if (existingState.ownerGid != mObbState.ownerGid) {
Slog.w(TAG, "Permission denied attempting to unmount OBB " + existingState.rawPath
+ " (owned by GID " + existingState.ownerGid + ")");
sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
notifyObbStateChange(new ObbException(ERROR_PERMISSION_DENIED,
"Permission denied to unmount OBB " + existingState.rawPath
+ " (owned by GID " + existingState.ownerGid + ")"));
return;
}
int rc = StorageResultCode.OperationSucceeded;
try {
mVold.unmount(mObbState.volId);
mVold.destroyObb(mObbState.volId);
mObbState.volId = null;
} catch (Exception e) {
Slog.w(TAG, e);
rc = StorageResultCode.OperationFailedInternalError;
}
if (rc == StorageResultCode.OperationSucceeded) {
synchronized (mObbMounts) {
removeObbStateLocked(existingState);
}
sendNewStatusOrIgnore(OnObbStateChangeListener.UNMOUNTED);
} else {
Slog.w(TAG, "Could not unmount OBB: " + existingState);
sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_COULD_NOT_UNMOUNT);
notifyObbStateChange(UNMOUNTED);
} catch (Exception e) {
throw new ObbException(ERROR_COULD_NOT_UNMOUNT, e);
}
}
@Override
public void handleError() {
sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();

View File

@@ -553,9 +553,9 @@ public class PackageManagerService extends IPackageManager.Stub
public static final String PLATFORM_PACKAGE_NAME = "android";
static final String DEFAULT_CONTAINER_PACKAGE = "com.android.defcontainer";
public static final String DEFAULT_CONTAINER_PACKAGE = "com.android.defcontainer";
static final ComponentName DEFAULT_CONTAINER_COMPONENT = new ComponentName(
public static final ComponentName DEFAULT_CONTAINER_COMPONENT = new ComponentName(
DEFAULT_CONTAINER_PACKAGE,
"com.android.defcontainer.DefaultContainerService");

View File

@@ -16,7 +16,7 @@
package com.android.server.pm.permission;
import static com.android.server.pm.PackageManagerServiceUtils.compareSignatures;
import static android.os.Process.FIRST_APPLICATION_UID;
import android.Manifest;
import android.annotation.NonNull;
@@ -28,15 +28,14 @@ import android.companion.CompanionDeviceManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageList;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.PackageManagerInternal.PackagesProvider;
import android.content.pm.PackageManagerInternal.SyncAdapterPackagesProvider;
import android.content.pm.PackageParser;
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.PackageManagerInternal.PackagesProvider;
import android.content.pm.PackageManagerInternal.SyncAdapterPackagesProvider;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Binder;
@@ -52,15 +51,17 @@ import android.provider.CalendarContract;
import android.provider.ContactsContract;
import android.provider.MediaStore;
import android.provider.Telephony.Sms.Intents;
import android.telephony.TelephonyManager;
import android.security.Credentials;
import android.telephony.TelephonyManager;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
import android.util.Slog;
import android.util.Xml;
import com.android.internal.util.XmlUtils;
import com.android.server.LocalServices;
import com.android.server.pm.PackageManagerService;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -71,14 +72,11 @@ import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static android.os.Process.FIRST_APPLICATION_UID;
/**
* This class is the policy for granting runtime permissions to
* platform components and default handlers in the system such
@@ -433,6 +431,13 @@ public final class DefaultPermissionGrantPolicy {
grantRuntimePermissions(storagePackage, STORAGE_PERMISSIONS, true, userId);
}
// Container service
PackageParser.Package containerPackage = getSystemPackage(
PackageManagerService.DEFAULT_CONTAINER_PACKAGE);
if (containerPackage != null) {
grantRuntimePermissions(containerPackage, STORAGE_PERMISSIONS, true, userId);
}
// CertInstaller
Intent certInstallerIntent = new Intent(Credentials.INSTALL_ACTION);
PackageParser.Package certInstallerPackage = getDefaultSystemHandlerActivityPackage(