Skip to content

Commit b1a5832

Browse files
joeyhuabneobuddy89
andcommitted
System: Switch Tensor feature handling to user-controlled override
Introduce a toggle to optionally enable Tensor-exclusive features on non-Tensor devices. Add persist.sys.pp.tensor to allow users to force-enable features defined in FEATURES_TENSOR on unsupported devices. When disabled, the system falls back to default feature detection. Tensor devices are not affected by this toggle and continue to rely on their native feature set. This follows a soft override approach, ensuring stock behavior is preserved unless explicitly enabled by the user, while avoiding issues previously caused by exposing these features globally. Also use features impl from crdroidandroid/android_frameworks_base@4af6e7d Co-authored-by: Pranav Vashi <neobuddy89@gmail.com>
1 parent 591a0dd commit b1a5832

2 files changed

Lines changed: 124 additions & 83 deletions

File tree

core/java/android/app/ApplicationPackageManager.java

Lines changed: 123 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -838,67 +838,132 @@ public Boolean recompute(HasSystemFeatureQuery query) {
838838
}
839839
};
840840

841-
private static final String[] featuresPixel = {
842-
"com.google.android.apps.photos.PIXEL_2019_PRELOAD",
843-
"com.google.android.apps.photos.PIXEL_2019_MIDYEAR_PRELOAD",
844-
"com.google.android.apps.photos.PIXEL_2018_PRELOAD",
845-
"com.google.android.apps.photos.PIXEL_2017_PRELOAD",
846-
"com.google.android.feature.PIXEL_2021_MIDYEAR_EXPERIENCE",
847-
"com.google.android.feature.PIXEL_2020_EXPERIENCE",
848-
"com.google.android.feature.PIXEL_2020_MIDYEAR_EXPERIENCE",
849-
"com.google.android.feature.PIXEL_2019_EXPERIENCE",
850-
"com.google.android.feature.PIXEL_2019_MIDYEAR_EXPERIENCE",
851-
"com.google.android.feature.PIXEL_2018_EXPERIENCE",
852-
"com.google.android.feature.PIXEL_2017_EXPERIENCE",
853-
"com.google.android.feature.PIXEL_EXPERIENCE",
854-
"com.google.android.feature.GOOGLE_BUILD",
855-
"com.google.android.feature.GOOGLE_EXPERIENCE"
856-
};
857-
858-
private static final String[] featuresPixelOthers = {
859-
"com.google.android.feature.ASI",
860-
"com.google.android.feature.ANDROID_ONE_EXPERIENCE",
861-
"com.google.android.feature.GOOGLE_FI_BUNDLED",
862-
"com.google.android.feature.LILY_EXPERIENCE",
863-
"com.google.android.feature.TURBO_PRELOAD",
864-
"com.google.android.feature.WELLBEING",
865-
"com.google.lens.feature.IMAGE_INTEGRATION",
866-
"com.google.lens.feature.CAMERA_INTEGRATION",
867-
"com.google.photos.trust_debug_certs",
868-
"com.google.android.feature.AER_OPTIMIZED",
869-
"com.google.android.feature.NEXT_GENERATION_ASSISTANT",
870-
"android.software.game_service",
871-
"com.google.android.feature.EXCHANGE_6_2",
872-
"com.google.android.apps.dialer.call_recording_audio",
873-
"com.google.android.apps.dialer.SUPPORTED",
874-
"com.google.android.feature.CONTEXTUAL_SEARCH",
875-
"com.google.android.feature.D2D_CABLE_MIGRATION_FEATURE"
876-
};
877-
878-
private static final String[] featuresTensor = {
879-
"com.google.android.feature.PIXEL_2026_EXPERIENCE",
880-
"com.google.android.feature.PIXEL_2026_MIDYEAR_EXPERIENCE",
881-
"com.google.android.feature.PIXEL_2025_EXPERIENCE",
882-
"com.google.android.feature.PIXEL_2025_MIDYEAR_EXPERIENCE",
883-
"com.google.android.feature.PIXEL_2024_EXPERIENCE",
884-
"com.google.android.feature.PIXEL_2024_MIDYEAR_EXPERIENCE",
885-
"com.google.android.feature.PIXEL_2023_EXPERIENCE",
886-
"com.google.android.feature.PIXEL_2023_MIDYEAR_EXPERIENCE",
887-
"com.google.android.feature.PIXEL_2022_EXPERIENCE",
888-
"com.google.android.feature.PIXEL_2022_MIDYEAR_EXPERIENCE",
889-
"com.google.android.feature.PIXEL_2021_EXPERIENCE"
890-
};
891-
892-
private static final String[] featuresNexus = {
893-
"com.google.android.apps.photos.NEXUS_PRELOAD",
894-
"com.google.android.apps.photos.nexus_preload",
895-
"com.google.android.feature.PIXEL_EXPERIENCE",
896-
"com.google.android.feature.GOOGLE_BUILD",
897-
"com.google.android.feature.GOOGLE_EXPERIENCE"
898-
};
841+
private static final ArraySet<String> PRIV_PKGS = new ArraySet<>();
842+
private static final ArraySet<String> FEATURES_PIXEL = new ArraySet<>();
843+
private static final ArraySet<String> FEATURES_PIXEL_OTHERS = new ArraySet<>();
844+
private static final ArraySet<String> FEATURES_TENSOR = new ArraySet<>();
845+
private static final ArraySet<String> FEATURES_NEXUS = new ArraySet<>();
846+
private static final ArraySet<String> TENSOR_CODENAMES = new ArraySet<>();
847+
private static final boolean IS_TENSOR_DEVICE;
848+
849+
static {
850+
Collections.addAll(FEATURES_PIXEL,
851+
"com.google.android.apps.photos.PIXEL_2019_PRELOAD",
852+
"com.google.android.apps.photos.PIXEL_2019_MIDYEAR_PRELOAD",
853+
"com.google.android.apps.photos.PIXEL_2018_PRELOAD",
854+
"com.google.android.apps.photos.PIXEL_2017_PRELOAD",
855+
"com.google.android.feature.PIXEL_2021_MIDYEAR_EXPERIENCE",
856+
"com.google.android.feature.PIXEL_2020_EXPERIENCE",
857+
"com.google.android.feature.PIXEL_2020_MIDYEAR_EXPERIENCE",
858+
"com.google.android.feature.PIXEL_2019_EXPERIENCE",
859+
"com.google.android.feature.PIXEL_2019_MIDYEAR_EXPERIENCE",
860+
"com.google.android.feature.PIXEL_2018_EXPERIENCE",
861+
"com.google.android.feature.PIXEL_2017_EXPERIENCE",
862+
"com.google.android.feature.PIXEL_EXPERIENCE",
863+
"com.google.android.feature.GOOGLE_BUILD",
864+
"com.google.android.feature.GOOGLE_EXPERIENCE"
865+
);
866+
867+
Collections.addAll(FEATURES_PIXEL_OTHERS,
868+
"com.google.android.feature.ASI",
869+
"com.google.android.feature.ANDROID_ONE_EXPERIENCE",
870+
"com.google.android.feature.GOOGLE_FI_BUNDLED",
871+
"com.google.android.feature.LILY_EXPERIENCE",
872+
"com.google.android.feature.TURBO_PRELOAD",
873+
"com.google.android.feature.WELLBEING",
874+
"com.google.lens.feature.IMAGE_INTEGRATION",
875+
"com.google.lens.feature.CAMERA_INTEGRATION",
876+
"com.google.photos.trust_debug_certs",
877+
"com.google.android.feature.AER_OPTIMIZED",
878+
"com.google.android.feature.NEXT_GENERATION_ASSISTANT",
879+
"android.software.game_service",
880+
"com.google.android.feature.EXCHANGE_6_2",
881+
"com.google.android.apps.dialer.call_recording_audio",
882+
"com.google.android.apps.dialer.SUPPORTED"
883+
);
884+
885+
Collections.addAll(FEATURES_TENSOR,
886+
"com.google.android.feature.PIXEL_2026_EXPERIENCE",
887+
"com.google.android.feature.PIXEL_2026_MIDYEAR_EXPERIENCE",
888+
"com.google.android.feature.PIXEL_2025_EXPERIENCE",
889+
"com.google.android.feature.PIXEL_2025_MIDYEAR_EXPERIENCE",
890+
"com.google.android.feature.PIXEL_2024_EXPERIENCE",
891+
"com.google.android.feature.PIXEL_2024_MIDYEAR_EXPERIENCE",
892+
"com.google.android.feature.PIXEL_2023_EXPERIENCE",
893+
"com.google.android.feature.PIXEL_2023_MIDYEAR_EXPERIENCE",
894+
"com.google.android.feature.PIXEL_2022_EXPERIENCE",
895+
"com.google.android.feature.PIXEL_2022_MIDYEAR_EXPERIENCE",
896+
"com.google.android.feature.PIXEL_2021_EXPERIENCE"
897+
);
898+
899+
Collections.addAll(FEATURES_NEXUS,
900+
"com.google.android.apps.photos.NEXUS_PRELOAD",
901+
"com.google.android.apps.photos.nexus_preload",
902+
"com.google.android.feature.PIXEL_EXPERIENCE",
903+
"com.google.android.feature.GOOGLE_BUILD",
904+
"com.google.android.feature.GOOGLE_EXPERIENCE"
905+
);
906+
907+
Collections.addAll(TENSOR_CODENAMES,
908+
"stallion","blazer","frankel","mustang","tegu","comet","komodo","caiman","tokay",
909+
"akita","husky","shiba","felix","tangorpro","lynx","cheetah","panther",
910+
"bluejay","oriole","raven"
911+
);
912+
913+
Collections.addAll(PRIV_PKGS,
914+
"com.google.android.googlequicksearchbox",
915+
"com.google.android.apps.photos",
916+
"com.google.android.apps.pixel.agent",
917+
"com.google.android.apps.pixel.creativeassistant"
918+
);
919+
920+
final String device = SystemProperties.get("ro.evolution.device");
921+
IS_TENSOR_DEVICE = TENSOR_CODENAMES.contains(device);
922+
}
899923

900924
@Override
901925
public boolean hasSystemFeature(String name, int version) {
926+
final String pkg = ActivityThread.currentPackageName();
927+
928+
if (name != null && pkg != null && PRIV_PKGS.contains(pkg)) {
929+
final boolean photosSpoof = "com.google.android.apps.photos".equals(pkg)
930+
&& SystemProperties.getBoolean("persist.sys.pp.photos", true);
931+
if (photosSpoof) {
932+
if (FEATURES_PIXEL.contains(name)) return false;
933+
if (FEATURES_PIXEL_OTHERS.contains(name)) return true;
934+
if (FEATURES_TENSOR.contains(name)) return false;
935+
if (FEATURES_NEXUS.contains(name)) return true;
936+
} else {
937+
if (FEATURES_PIXEL.contains(name)) return true;
938+
if (FEATURES_PIXEL_OTHERS.contains(name)) return true;
939+
if (FEATURES_TENSOR.contains(name)) return true;
940+
if (FEATURES_NEXUS.contains(name)) return true;
941+
}
942+
}
943+
944+
if (name != null && FEATURES_TENSOR.contains(name)) {
945+
final boolean forceTensor = SystemProperties.getBoolean(
946+
"persist.sys.pp.tensor", false);
947+
948+
// Do not interfere with real Tensor devices
949+
if (IS_TENSOR_DEVICE) {
950+
return mHasSystemFeatureCache.query(
951+
new HasSystemFeatureQuery(name, version));
952+
}
953+
954+
// Only override if user explicitly enabled the toggle
955+
if (forceTensor) {
956+
return true;
957+
}
958+
959+
// Otherwise, behave like stock
960+
return mHasSystemFeatureCache.query(
961+
new HasSystemFeatureQuery(name, version));
962+
}
963+
964+
if (name != null && FEATURES_PIXEL.contains(name)) return true;
965+
if (name != null && FEATURES_PIXEL_OTHERS.contains(name)) return true;
966+
902967
// We check for system features in the following order:
903968
// * Build time-defined system features (constant, very efficient)
904969
// * SDK-defined system features (cached at process start, very efficient)
@@ -916,31 +981,6 @@ public boolean hasSystemFeature(String name, int version) {
916981
return maybeHasSystemFeature;
917982
}
918983
}
919-
920-
String packageName = ActivityThread.currentPackageName();
921-
boolean isPhotosSpoofEnabled = SystemProperties.getBoolean("persist.sys.pp.photos", true);
922-
if (packageName != null
923-
&& (packageName.equals("com.google.android.googlequicksearchbox")
924-
|| packageName.equals("com.google.android.apps.pixel.agent")
925-
|| packageName.equals("com.google.android.apps.pixel.creativeassistant")
926-
|| packageName.equals("com.google.android.dialer")
927-
|| (packageName.equals("com.google.android.apps.photos")
928-
&& !isPhotosSpoofEnabled))) {
929-
if (Arrays.asList(featuresPixel).contains(name)) return true;
930-
if (Arrays.asList(featuresPixelOthers).contains(name)) return true;
931-
if (Arrays.asList(featuresTensor).contains(name)) return true;
932-
if (Arrays.asList(featuresNexus).contains(name)) return true;
933-
}
934-
if (packageName != null
935-
&& packageName.equals("com.google.android.apps.photos") && isPhotosSpoofEnabled) {
936-
if (Arrays.asList(featuresPixel).contains(name)) return false;
937-
if (Arrays.asList(featuresPixelOthers).contains(name)) return true;
938-
if (Arrays.asList(featuresTensor).contains(name)) return false;
939-
if (Arrays.asList(featuresNexus).contains(name)) return true;
940-
}
941-
if (Arrays.asList(featuresNexus).contains(name)) return true;
942-
if (Arrays.asList(featuresPixel).contains(name)) return true;
943-
if (Arrays.asList(featuresPixelOthers).contains(name)) return true;
944984
return mHasSystemFeatureCache.query(new HasSystemFeatureQuery(name, version));
945985
}
946986

services/core/java/com/android/server/am/ActiveServices.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2912,6 +2912,7 @@ private Pair<Integer, RuntimeException> validateForegroundServiceType(ServiceRec
29122912

29132913
// Whitelist of package names to bypass FGS type validation
29142914
final Set<String> whitelistPackages = new HashSet<>(Arrays.asList(
2915+
"com.google.android.as", // Google Device Personalization services
29152916
"com.google.android.gms", // Google Play Services
29162917
"com.android.vending", // Google Play Store
29172918
"com.google.android.gsf", // Google Services Framework

0 commit comments

Comments
 (0)