Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions core/api/system-current.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4365,6 +4365,7 @@ package android.content.pm {
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.GosPackageState> CREATOR;
field @Nullable public final byte[] contactScopes;
field @Nullable public final byte[] microphoneScopes;
field @Nullable public final byte[] storageScopes;
}

Expand All @@ -4378,13 +4379,15 @@ package android.content.pm {
method @NonNull public android.content.pm.GosPackageState.Editor setContactScopes(@Nullable byte[]);
method @NonNull public android.content.pm.GosPackageState.Editor setFlagState(int, boolean);
method @NonNull public android.content.pm.GosPackageState.Editor setKillUidAfterApply(boolean);
method @NonNull public android.content.pm.GosPackageState.Editor setMicrophoneScopes(@Nullable byte[]);
method @NonNull public android.content.pm.GosPackageState.Editor setNotifyUidAfterApply(boolean);
method @NonNull public android.content.pm.GosPackageState.Editor setPackageFlagState(int, boolean);
method @NonNull public android.content.pm.GosPackageState.Editor setStorageScopes(@Nullable byte[]);
}

public interface GosPackageStateFlag {
field public static final int CONTACT_SCOPES_ENABLED = 5; // 0x5
field public static final int MICROPHONE_SCOPES_ENABLED = 29; // 0x1d
field public static final int STORAGE_SCOPES_ENABLED = 0; // 0x0
}

Expand Down Expand Up @@ -5275,6 +5278,7 @@ package android.ext {
field public static final int HAS_READ_MEDIA_IMAGES_DECLARATION = 1024; // 0x400
field public static final int HAS_READ_MEDIA_VIDEO_DECLARATION = 2048; // 0x800
field public static final int HAS_READ_MEDIA_VISUAL_USER_SELECTED_DECLARATION = 4096; // 0x1000
field public static final int HAS_RECORD_AUDIO_DECLARATION = 8388608; // 0x800000
field public static final int HAS_WRITE_CONTACTS_DECLARATION = 2097152; // 0x200000
field public static final int HAS_WRITE_EXTERNAL_STORAGE_DECLARATION = 32; // 0x20
}
Expand Down
2 changes: 2 additions & 0 deletions core/java/android/app/ActivityThreadHooks.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import android.util.Log;

import com.android.internal.app.ContactScopes;
import com.android.internal.app.MicrophoneScopes;
import com.android.internal.app.StorageScopesAppHooks;
import com.android.internal.gmscompat.GmsHooks;

Expand Down Expand Up @@ -74,6 +75,7 @@ static void onBind2(Context appContext, Bundle appBindArgs) {
static void onGosPackageStateChanged(Context ctx, GosPackageState state, boolean fromBind) {
StorageScopesAppHooks.maybeEnable(state);
ContactScopes.maybeEnable(ctx, state);
MicrophoneScopes.maybeEnable(ctx, state);
}

static Service instantiateService(String className) {
Expand Down
16 changes: 16 additions & 0 deletions core/java/android/content/pm/AppPermissionUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import android.app.compat.gms.GmsCompat;

import com.android.internal.app.ContactScopes;
import com.android.internal.app.MicrophoneScopes;
import com.android.internal.app.StorageScopesAppHooks;
import com.android.internal.gmscompat.GmsHooks;

Expand All @@ -47,6 +48,10 @@ public static boolean shouldSpoofSelfCheck(String permName) {
return true;
}

if (MicrophoneScopes.shouldSpoofSelfPermissionCheck(permName)) {
return true;
}

if (GmsCompat.isEnabled()) {
if (GmsHooks.config().shouldSpoofSelfPermissionCheck(permName)) {
return true;
Expand All @@ -70,6 +75,10 @@ public static boolean shouldSpoofSelfAppOpCheck(int op) {
return true;
}

if (MicrophoneScopes.shouldSpoofSelfAppOpCheck(op)) {
return true;
}

return false;
}

Expand Down Expand Up @@ -108,6 +117,13 @@ private static int getSpoofablePermissionDflag(GosPackageState ps, String perm,
}
}

if (ps.hasFlag(GosPackageStateFlag.MICROPHONE_SCOPES_ENABLED)) {
int permDflag = MicrophoneScopes.getSpoofablePermissionDflag(perm);
if (permDflag != 0) {
return permDflag;
}
}

return 0;
}

Expand Down
26 changes: 21 additions & 5 deletions core/java/android/content/pm/GosPackageState.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ public final class GosPackageState implements Parcelable {
public final byte[] storageScopes;
@Nullable
public final byte[] contactScopes;
@Nullable
public final byte[] microphoneScopes;
/**
* These flags are lazily derived from persistent state. They are intentionally skipped from
* equals() and hashCode(). derivedFlags are stored here for performance reasons, to avoid
Expand All @@ -63,15 +65,17 @@ public final class GosPackageState implements Parcelable {

/** @hide */
public GosPackageState(long flagStorage1, long packageFlagStorage,
@Nullable byte[] storageScopes, @Nullable byte[] contactScopes) {
@Nullable byte[] storageScopes, @Nullable byte[] contactScopes,
@Nullable byte[] microphoneScopes) {
this.flagStorage1 = flagStorage1;
this.packageFlagStorage = packageFlagStorage;
this.storageScopes = storageScopes;
this.contactScopes = contactScopes;
this.microphoneScopes = microphoneScopes;
}

private static GosPackageState createEmpty() {
return new GosPackageState(0L, 0L, null, null);
return new GosPackageState(0L, 0L, null, null, null);
}

private static final int TYPE_NONE = 0;
Expand All @@ -94,6 +98,7 @@ public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeLong(this.packageFlagStorage);
dest.writeByteArray(storageScopes);
dest.writeByteArray(contactScopes);
dest.writeByteArray(microphoneScopes);
dest.writeInt(derivedFlags);
}

Expand All @@ -106,7 +111,7 @@ public GosPackageState createFromParcel(Parcel in) {
case TYPE_NONE: return NONE;
};
var res = new GosPackageState(in.readLong(), in.readLong(),
in.createByteArray(), in.createByteArray());
in.createByteArray(), in.createByteArray(), in.createByteArray());
res.derivedFlags = in.readInt();
return res;
}
Expand All @@ -119,7 +124,7 @@ public GosPackageState[] newArray(int size) {

@Override
public int hashCode() {
return Long.hashCode(flagStorage1) + Arrays.hashCode(storageScopes) + Arrays.hashCode(contactScopes) + Long.hashCode(packageFlagStorage);
return Long.hashCode(flagStorage1) + Arrays.hashCode(storageScopes) + Arrays.hashCode(contactScopes) + Arrays.hashCode(microphoneScopes) + Long.hashCode(packageFlagStorage);
}

@Override
Expand All @@ -139,6 +144,9 @@ public boolean equals(Object obj) {
if (!Arrays.equals(contactScopes, o.contactScopes)) {
return false;
}
if (!Arrays.equals(microphoneScopes, o.microphoneScopes)) {
return false;
}
if (packageFlagStorage != o.packageFlagStorage) {
return false;
}
Expand Down Expand Up @@ -236,6 +244,7 @@ public static class Editor {
private long packageFlagStorage;
private byte[] storageScopes;
private byte[] contactScopes;
private byte[] microphoneScopes;
private int editorFlags;

/** @hide */
Expand All @@ -246,6 +255,7 @@ public Editor(GosPackageState s, String packageName, int userId) {
this.packageFlagStorage = s.packageFlagStorage;
this.storageScopes = s.storageScopes;
this.contactScopes = s.contactScopes;
this.microphoneScopes = s.microphoneScopes;
}

@NonNull
Expand Down Expand Up @@ -304,6 +314,12 @@ public Editor setContactScopes(@Nullable byte[] contactScopes) {
return this;
}

@NonNull
public Editor setMicrophoneScopes(@Nullable byte[] microphoneScopes) {
this.microphoneScopes = microphoneScopes;
return this;
}

@NonNull
public Editor killUidAfterApply() {
return setKillUidAfterApply(true);
Expand Down Expand Up @@ -334,7 +350,7 @@ public Editor setNotifyUidAfterApply(boolean v) {
public boolean apply() {
try {
return ActivityThread.getPackageManager().setGosPackageState(packageName, userId,
new GosPackageState(flagStorage1, packageFlagStorage, storageScopes, contactScopes),
new GosPackageState(flagStorage1, packageFlagStorage, storageScopes, contactScopes, microphoneScopes),
editorFlags);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
Expand Down
2 changes: 2 additions & 0 deletions core/java/android/content/pm/GosPackageStateFlag.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ public interface GosPackageStateFlag {
/** @hide */ int PLAY_INTEGRITY_API_USED_AT_LEAST_ONCE = 26;
/** @hide */ int SUPPRESS_PLAY_INTEGRITY_API_NOTIF = 27;
/** @hide */ int BLOCK_PLAY_INTEGRITY_API = 28;
/* SysApi */ int MICROPHONE_SCOPES_ENABLED = 29;

/** @hide */
@IntDef(value = {
Expand Down Expand Up @@ -64,6 +65,7 @@ public interface GosPackageStateFlag {
PLAY_INTEGRITY_API_USED_AT_LEAST_ONCE,
SUPPRESS_PLAY_INTEGRITY_API_NOTIF,
BLOCK_PLAY_INTEGRITY_API,
MICROPHONE_SCOPES_ENABLED,
})
@Retention(RetentionPolicy.SOURCE)
@interface Enum {}
Expand Down
2 changes: 2 additions & 0 deletions core/java/android/ext/DerivedPackageFlag.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public interface DerivedPackageFlag {
int HAS_READ_CONTACTS_DECLARATION = 1 << 20;
int HAS_WRITE_CONTACTS_DECLARATION = 1 << 21;
int HAS_GET_ACCOUNTS_DECLARATION = 1 << 22;
int HAS_RECORD_AUDIO_DECLARATION = 1 << 23;

/** @hide */
@IntDef(flag = true, value = {
Expand All @@ -47,6 +48,7 @@ public interface DerivedPackageFlag {
HAS_READ_CONTACTS_DECLARATION,
HAS_WRITE_CONTACTS_DECLARATION,
HAS_GET_ACCOUNTS_DECLARATION,
HAS_RECORD_AUDIO_DECLARATION,
})
@Retention(RetentionPolicy.SOURCE)
@interface Enum {}
Expand Down
72 changes: 72 additions & 0 deletions core/java/com/android/internal/app/MicrophoneScopes.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package com.android.internal.app;

import android.Manifest;
import android.annotation.AnyThread;
import android.app.AppOpsManager;
import android.content.Context;
import android.content.pm.GosPackageState;
import android.content.pm.GosPackageStateFlag;
import android.ext.DerivedPackageFlag;

public class MicrophoneScopes {
private static volatile boolean isEnabled;
private static int gosPsDerivedFlags;

public static boolean isEnabled() {
return isEnabled;
}

@AnyThread
public static void maybeEnable(Context ctx, GosPackageState ps) {
synchronized (MicrophoneScopes.class) {
if (isEnabled) {
return;
}

if (ps.hasFlag(GosPackageStateFlag.MICROPHONE_SCOPES_ENABLED)) {
gosPsDerivedFlags = ps.derivedFlags;
isEnabled = true;
}
}
}

// call only if isEnabled is true
private static boolean shouldSpoofPermissionCheckInner(int permDflag) {
if (permDflag == 0) {
return false;
}
return (gosPsDerivedFlags & permDflag) != 0;
}

public static boolean shouldSpoofSelfPermissionCheck(String permName) {
if (!isEnabled) {
return false;
}
return shouldSpoofPermissionCheckInner(getSpoofablePermissionDflag(permName));
}

public static boolean shouldSpoofSelfAppOpCheck(int op) {
if (!isEnabled) {
return false;
}
return shouldSpoofPermissionCheckInner(getSpoofableAppOpPermissionDflag(op));
}

public static int getSpoofablePermissionDflag(String permName) {
switch (permName) {
case Manifest.permission.RECORD_AUDIO:
return DerivedPackageFlag.HAS_RECORD_AUDIO_DECLARATION;
default:
return 0;
}
}

private static int getSpoofableAppOpPermissionDflag(int op) {
switch (op) {
case AppOpsManager.OP_RECORD_AUDIO:
return DerivedPackageFlag.HAS_RECORD_AUDIO_DECLARATION;
default:
return 0;
}
}
}
37 changes: 30 additions & 7 deletions services/core/java/com/android/server/am/ActiveServices.java
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,8 @@
import android.content.pm.PackageManagerInternal;
import android.content.pm.ParceledListSlice;
import android.content.pm.ResolveInfo;
import android.content.pm.GosPackageState;
import android.content.pm.GosPackageStateFlag;
import android.content.pm.ServiceInfo;
import android.content.pm.ServiceInfo.ForegroundServiceType;
import android.os.Build;
Expand Down Expand Up @@ -2644,13 +2646,34 @@ must call startForeground() within a timeout anyway, so we don't need this
}
fgsTypeCheckCode = fgsTypeResult.first;
if (fgsTypeResult.second != null) {
logFGSStateChangeLocked(r,
FOREGROUND_SERVICE_STATE_CHANGED__STATE__DENIED,
0, FGS_STOP_REASON_UNKNOWN, fgsTypeResult.first,
FOREGROUND_SERVICE_STATE_CHANGED__FGS_START_API__FGSSTARTAPI_NA,
false /* fgsRestrictionRecalculated */
);
throw fgsTypeResult.second;
if ((foregroundServiceType
& ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE) != 0) {
int userId = UserHandle.getUserId(r.appInfo.uid);
GosPackageState gosPs = GosPackageState.get(
r.packageName, userId);
if (gosPs.hasFlag(
GosPackageStateFlag.MICROPHONE_SCOPES_ENABLED)) {
Slog.i(TAG, "Allowing microphone FGS for app with"
+ " microphone scopes: " + r.packageName);
fgsTypeCheckCode = FGS_TYPE_POLICY_CHECK_OK;
} else {
logFGSStateChangeLocked(r,
FOREGROUND_SERVICE_STATE_CHANGED__STATE__DENIED,
0, FGS_STOP_REASON_UNKNOWN, fgsTypeResult.first,
FOREGROUND_SERVICE_STATE_CHANGED__FGS_START_API__FGSSTARTAPI_NA,
false /* fgsRestrictionRecalculated */
);
throw fgsTypeResult.second;
}
} else {
logFGSStateChangeLocked(r,
FOREGROUND_SERVICE_STATE_CHANGED__STATE__DENIED,
0, FGS_STOP_REASON_UNKNOWN, fgsTypeResult.first,
FOREGROUND_SERVICE_STATE_CHANGED__FGS_START_API__FGSSTARTAPI_NA,
false /* fgsRestrictionRecalculated */
);
throw fgsTypeResult.second;
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,10 @@ class GosPackageStatePermission {
static final int FIELD_STORAGE_SCOPES = 0;
static final int FIELD_CONTACT_SCOPES = 1;
static final int FIELD_PACKAGE_FLAGS = 2;
static final int FIELD_MICROPHONE_SCOPES = 3;

@IntDef(prefix = "FIELD_", value = {
FIELD_STORAGE_SCOPES, FIELD_CONTACT_SCOPES, FIELD_PACKAGE_FLAGS
FIELD_STORAGE_SCOPES, FIELD_CONTACT_SCOPES, FIELD_PACKAGE_FLAGS, FIELD_MICROPHONE_SCOPES
})
@Retention(RetentionPolicy.SOURCE)
@interface Field {}
Expand Down Expand Up @@ -214,6 +215,7 @@ GosPackageState filterRead(GosPackageState ps) {
, canReadField(FIELD_PACKAGE_FLAGS) ? ps.packageFlagStorage : default_.packageFlagStorage
, canReadField(FIELD_STORAGE_SCOPES) ? ps.storageScopes : default_.storageScopes
, canReadField(FIELD_CONTACT_SCOPES) ? ps.contactScopes : default_.contactScopes
, canReadField(FIELD_MICROPHONE_SCOPES) ? ps.microphoneScopes : default_.microphoneScopes
);
if (default_.equals(res)) {
return default_;
Expand All @@ -235,6 +237,7 @@ GosPackageState filterWrite(GosPackageState current, GosPackageState update) {
, canWriteField(FIELD_PACKAGE_FLAGS) ? update.packageFlagStorage : current.packageFlagStorage
, canWriteField(FIELD_STORAGE_SCOPES) ? update.storageScopes : current.storageScopes
, canWriteField(FIELD_CONTACT_SCOPES) ? update.contactScopes : current.contactScopes
, canWriteField(FIELD_MICROPHONE_SCOPES) ? update.microphoneScopes : current.microphoneScopes
);
var default_ = GosPackageState.DEFAULT;
if (default_.equals(res)) {
Expand Down
Loading