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
23 changes: 23 additions & 0 deletions core/api/system-current.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4319,6 +4319,7 @@ package android.content.om {
package android.content.pm {

public class AppPermissionUtils {
method public static boolean shouldHideRequestPermissionRationale(@NonNull android.content.pm.GosPackageState, @NonNull String);
method public static boolean shouldSkipPermissionRequestDialog(@NonNull android.content.pm.GosPackageState, @NonNull String);
method public static boolean shouldSpoofPermissionRequestResult(@NonNull android.content.pm.GosPackageState, @NonNull String);
}
Expand Down Expand Up @@ -4366,6 +4367,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[] micSpoofingConfig;
field @Nullable public final byte[] storageScopes;
}

Expand All @@ -4379,13 +4381,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 setMicSpoofingConfig(@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 MIC_SPOOFING_ENABLED = 29; // 0x1d
field public static final int STORAGE_SCOPES_ENABLED = 0; // 0x0
}

Expand Down Expand Up @@ -5276,6 +5280,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 Expand Up @@ -5392,6 +5397,24 @@ package android.ext.dcl {

}

package android.ext.micspoofing {

public final class MicSpoofingApi {
method @NonNull public static byte[] buildCustomPathConfig(@NonNull String);
method @NonNull public static byte[] buildDefaultConfig();
method @NonNull public static android.content.Intent createConfigActivityIntent(@NonNull String);
method @Nullable public static String getCustomAudioPathForApp(@NonNull String, int);
method @Nullable public static String getPath(@Nullable byte[]);
method public static int getSourceMode(@Nullable byte[]);
field public static final String MEDIA_PROVIDER_METHOD_OPEN_SOURCE = "MicSpoofing_openSource";
field public static final String MEDIA_PROVIDER_RESULT_KEY_SOURCE_FD = "MicSpoofing_sourceFd";
field public static final int MODE_CUSTOM_PATH = 1; // 0x1
field public static final int MODE_DEFAULT = 0; // 0x0
field public static final int VERSION = 1; // 0x1
}

}

package android.ext.settings {

public class BoolSetting extends android.ext.settings.Setting<android.ext.settings.BoolSetting> {
Expand Down
3 changes: 2 additions & 1 deletion core/java/android/app/ActivityThreadHooks.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package android.app;

import android.annotation.Nullable;
import android.content.Context;
import android.content.pm.GosPackageState;
import android.content.pm.SrtPermissions;
import android.content.pm.spoofing.MicSpoofing;
import android.ext.dcl.DynCodeLoading;
import android.location.HookedLocationManager;
import android.os.Bundle;
Expand Down Expand Up @@ -74,6 +74,7 @@ static void onBind2(Context appContext, Bundle appBindArgs) {
static void onGosPackageStateChanged(Context ctx, GosPackageState state, boolean fromBind) {
StorageScopesAppHooks.maybeEnable(state);
ContactScopes.maybeEnable(ctx, state);
MicSpoofing.onGosPackageStateChanged(state);
}

static Service instantiateService(String className) {
Expand Down
42 changes: 42 additions & 0 deletions core/java/android/content/pm/AppPermissionUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,12 @@

package android.content.pm;

import android.Manifest;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.app.compat.gms.GmsCompat;
import android.content.pm.spoofing.MicSpoofing;
import android.ext.DerivedPackageFlag;

import com.android.internal.app.ContactScopes;
import com.android.internal.app.StorageScopesAppHooks;
Expand Down Expand Up @@ -53,6 +56,10 @@ public static boolean shouldSpoofSelfCheck(String permName) {
}
}

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

return false;
}

Expand All @@ -70,6 +77,10 @@ public static boolean shouldSpoofSelfAppOpCheck(int op) {
return true;
}

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

return false;
}

Expand All @@ -82,6 +93,30 @@ public static boolean shouldSkipPermissionRequestDialog(@NonNull GosPackageState
return getSpoofablePermissionDflag(ps, perm, true) != 0;
}

public static boolean shouldHideRequestPermissionRationale(
@NonNull GosPackageState packageState,
@NonNull String permission
) {
int permDflag = getRationaleHidePermissionDflag(packageState, permission);
return permDflag != 0 && packageState.hasDerivedFlag(permDflag);
}

private static int getRationaleHidePermissionDflag(
@NonNull GosPackageState packageState,
@NonNull String permission
) {
//noinspection SwitchStatementWithTooFewBranches,EnhancedSwitchMigration
switch (permission) {
case Manifest.permission.RECORD_AUDIO:
if (!packageState.hasFlag(GosPackageStateFlag.MIC_SPOOFING_ENABLED)) {
return 0;
}
return DerivedPackageFlag.HAS_RECORD_AUDIO_DECLARATION;
default:
return 0;
}
}

// Controls spoofing of Activity#onRequestPermissionsResult() callback
public static boolean shouldSpoofPermissionRequestResult(@NonNull GosPackageState ps, @NonNull String perm) {
int dflag = getSpoofablePermissionDflag(ps, perm, false);
Expand All @@ -108,6 +143,13 @@ private static int getSpoofablePermissionDflag(GosPackageState ps, String perm,
}
}

if (ps.hasFlag(GosPackageStateFlag.MIC_SPOOFING_ENABLED)) {
int permDflag = MicSpoofing.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[] micSpoofingConfig;
/**
* 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[] micSpoofingConfig) {
this.flagStorage1 = flagStorage1;
this.packageFlagStorage = packageFlagStorage;
this.storageScopes = storageScopes;
this.contactScopes = contactScopes;
this.micSpoofingConfig = micSpoofingConfig;
}

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(micSpoofingConfig);
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) + Long.hashCode(packageFlagStorage) + Arrays.hashCode(micSpoofingConfig);
}

@Override
Expand All @@ -142,6 +147,9 @@ public boolean equals(Object obj) {
if (packageFlagStorage != o.packageFlagStorage) {
return false;
}
if (!Arrays.equals(micSpoofingConfig, o.micSpoofingConfig)) {
return false;
}
return true;
}

Expand Down Expand Up @@ -236,6 +244,7 @@ public static class Editor {
private long packageFlagStorage;
private byte[] storageScopes;
private byte[] contactScopes;
private byte[] micSpoofingConfig;
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.micSpoofingConfig = s.micSpoofingConfig;
}

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

@NonNull
public Editor setMicSpoofingConfig(@Nullable byte[] micSpoofingConfig) {
this.micSpoofingConfig = micSpoofingConfig;
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, micSpoofingConfig),
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 MIC_SPOOFING_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,
MIC_SPOOFING_ENABLED,
})
@Retention(RetentionPolicy.SOURCE)
@interface Enum {}
Expand Down
68 changes: 68 additions & 0 deletions core/java/android/content/pm/spoofing/MicSpoofing.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package android.content.pm.spoofing;

import android.Manifest;
import android.annotation.NonNull;
import android.app.AppOpsManager;
import android.content.pm.GosPackageState;
import android.content.pm.GosPackageStateFlag;
import android.ext.DerivedPackageFlag;
import android.util.Log;

/** @hide */
public class MicSpoofing {

private static final String TAG = "MicSpoofing";
private static final boolean VERBOSE_LOGGING = true;

private static volatile boolean isEnabled;
private static int gosPackageStateDerivedFlags;

private MicSpoofing() {
}

public static void onGosPackageStateChanged(GosPackageState gosPackageState) {
boolean shouldBeEnabled = gosPackageState.hasFlag(GosPackageStateFlag.MIC_SPOOFING_ENABLED);
if (shouldBeEnabled == isEnabled) return;

if (shouldBeEnabled) {
gosPackageStateDerivedFlags = gosPackageState.derivedFlags;
}

if (VERBOSE_LOGGING) {
Log.d(TAG, "Changing mic spoofing to " + shouldBeEnabled);
}

isEnabled = shouldBeEnabled;
}

public static boolean isEnabled() {
return isEnabled;
}

public static boolean shouldSpoofSelfPermissionCheck(@NonNull String permissionName) {
if (!isEnabled) return false;

return shouldSpoofPermissionCheckInner(getSpoofablePermissionDflag(permissionName));
}

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

return op == AppOpsManager.OP_RECORD_AUDIO && shouldSpoofPermissionCheckInner(
DerivedPackageFlag.HAS_RECORD_AUDIO_DECLARATION);
}

public static int getSpoofablePermissionDflag(@NonNull String permissionName) {
if (permissionName.equals(Manifest.permission.RECORD_AUDIO)) {
return DerivedPackageFlag.HAS_RECORD_AUDIO_DECLARATION;
}

return 0;
}

private static boolean shouldSpoofPermissionCheckInner(int permissionDflag) {
if (permissionDflag == 0) return false;

return (gosPackageStateDerivedFlags & permissionDflag) != 0;
}
}
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
Loading