Loading AndroidManifest.xml +22 −0 Original line number Diff line number Diff line Loading @@ -231,6 +231,28 @@ </intent-filter> </receiver> <receiver android:name=".development.Enable16KBootReceiver" android:enabled="true" android:exported="false"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> </intent-filter> </receiver> <service android:name=".development.PageAgnosticNotificationService" android:enabled="true" android:exported="false" android:permission="android.permission.POST_NOTIFICATIONS"/> <activity android:name=".development.PageAgnosticWarningActivity" android:enabled="true" android:launchMode="singleTask" android:taskAffinity="" android:excludeFromRecents="true" android:theme="@*android:style/Theme.DeviceDefault.Dialog.Alert.DayNight"/> <activity android:name=".SubSettings" android:exported="false" android:theme="@style/Theme.SubSettings" Loading src/com/android/settings/development/Enable16KBootReceiver.java 0 → 100644 +44 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 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.settings.development; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.os.UserHandle; import androidx.annotation.NonNull; public class Enable16KBootReceiver extends BroadcastReceiver { @Override public void onReceive(@NonNull Context context, @NonNull Intent intent) { String action = intent.getAction(); if (!Intent.ACTION_BOOT_COMPLETED.equals(action)) { return; } // Do nothing if device is not in page-agnostic mode if (!Enable16kUtils.isPageAgnosticModeOn(context)) { return; } // start a service to post persistent notification Intent startNotificationIntent = new Intent(context, PageAgnosticNotificationService.class); context.startServiceAsUser(startNotificationIntent, UserHandle.SYSTEM); } } src/com/android/settings/development/Enable16kPagesPreferenceController.java +6 −65 Original line number Diff line number Diff line Loading @@ -23,17 +23,11 @@ import android.os.ParcelFileDescriptor; import android.os.PersistableBundle; import android.os.PowerManager; import android.os.RecoverySystem; import android.os.SystemProperties; import android.os.SystemUpdateManager; import android.os.UpdateEngine; import android.os.UpdateEngineStable; import android.os.UpdateEngineStableCallback; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; import android.service.oemlock.OemLockManager; import android.system.Os; import android.system.OsConstants; import android.util.Log; import android.widget.LinearLayout; import android.widget.ProgressBar; Loading @@ -59,7 +53,6 @@ import com.google.common.util.concurrent.MoreExecutors; import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; Loading @@ -80,10 +73,6 @@ public class Enable16kPagesPreferenceController extends DeveloperOptionsPreferen private static final String TAG = "Enable16kPages"; private static final String REBOOT_REASON = "toggle16k"; private static final String ENABLE_16K_PAGES = "enable_16k_pages"; @VisibleForTesting static final String DEV_OPTION_PROPERTY = "ro.product.build.16k_page.enabled"; private static final int ENABLE_4K_PAGE_SIZE = 0; private static final int ENABLE_16K_PAGE_SIZE = 1; Loading @@ -97,9 +86,6 @@ public class Enable16kPagesPreferenceController extends DeveloperOptionsPreferen private static final int OFFSET_TO_FILE_NAME = 30; public static final String EXPERIMENTAL_UPDATE_TITLE = "Android 16K Kernel Experimental Update"; private static final long PAGE_SIZE = Os.sysconf(OsConstants._SC_PAGESIZE); private static final int PAGE_SIZE_16KB = 16 * 1024; private @NonNull DevelopmentSettingsDashboardFragment mFragment; private boolean mEnable16k; Loading @@ -112,12 +98,12 @@ public class Enable16kPagesPreferenceController extends DeveloperOptionsPreferen @NonNull Context context, @NonNull DevelopmentSettingsDashboardFragment fragment) { super(context); this.mFragment = fragment; mEnable16k = (PAGE_SIZE == PAGE_SIZE_16KB); mEnable16k = Enable16kUtils.isUsing16kbPages(); } @Override public boolean isAvailable() { return SystemProperties.getBoolean(DEV_OPTION_PROPERTY, false); return Enable16kUtils.is16KbToggleAvailable(); } @Override Loading @@ -129,12 +115,12 @@ public class Enable16kPagesPreferenceController extends DeveloperOptionsPreferen public boolean onPreferenceChange(Preference preference, Object newValue) { mEnable16k = (Boolean) newValue; // Prompt user to do oem unlock first if (!isDeviceOEMUnlocked()) { if (!Enable16kUtils.isDeviceOEMUnlocked(mContext)) { Enable16KOemUnlockDialog.show(mFragment); return false; } if (isDataf2fs()) { if (!Enable16kUtils.isDataExt4()) { EnableExt4WarningDialog.show(mFragment, this); return false; } Loading @@ -145,7 +131,7 @@ public class Enable16kPagesPreferenceController extends DeveloperOptionsPreferen @Override public void updateState(Preference preference) { int defaultOptionValue = PAGE_SIZE == PAGE_SIZE_16KB ? ENABLE_16K_PAGE_SIZE : ENABLE_4K_PAGE_SIZE; Enable16kUtils.isUsing16kbPages() ? ENABLE_16K_PAGE_SIZE : ENABLE_4K_PAGE_SIZE; final int optionValue = Settings.Global.getInt( mContext.getContentResolver(), Loading @@ -169,7 +155,7 @@ public class Enable16kPagesPreferenceController extends DeveloperOptionsPreferen @Override protected void onDeveloperOptionsSwitchEnabled() { int currentStatus = PAGE_SIZE == PAGE_SIZE_16KB ? ENABLE_16K_PAGE_SIZE : ENABLE_4K_PAGE_SIZE; Enable16kUtils.isUsing16kbPages() ? ENABLE_16K_PAGE_SIZE : ENABLE_4K_PAGE_SIZE; Settings.Global.putInt( mContext.getContentResolver(), Settings.Global.ENABLE_16K_PAGES, currentStatus); } Loading Loading @@ -432,51 +418,6 @@ public class Enable16kPagesPreferenceController extends DeveloperOptionsPreferen return infoBundle; } private boolean isDataf2fs() { try (BufferedReader br = new BufferedReader(new FileReader("/proc/mounts"))) { String line; while ((line = br.readLine()) != null) { final String[] fields = line.split(" "); final String partition = fields[1]; final String fsType = fields[2]; if (partition.equals("/data") && fsType.equals("f2fs")) { return true; } } } catch (IOException e) { Log.e(TAG, "Failed to read /proc/mounts"); displayToast(mContext.getString(R.string.format_ext4_failure_toast)); } return false; } private boolean isDeviceOEMUnlocked() { // OEM unlock is checked for bootloader, carrier and user. Check all three to ensure // that device is unlocked and it is also allowed by user as well as carrier final OemLockManager oemLockManager = mContext.getSystemService(OemLockManager.class); final UserManager userManager = mContext.getSystemService(UserManager.class); if (oemLockManager == null || userManager == null) { Log.e(TAG, "Required services not found on device to check for OEM unlock state."); return false; } // If either of device or carrier is not allowed to unlock, return false if (!oemLockManager.isDeviceOemUnlocked() || !oemLockManager.isOemUnlockAllowedByCarrier()) { Log.e(TAG, "Device is not OEM unlocked or it is not allowed by carrier"); return false; } final UserHandle userHandle = UserHandle.of(UserHandle.myUserId()); if (userManager.hasBaseUserRestriction(UserManager.DISALLOW_FACTORY_RESET, userHandle)) { Log.e(TAG, "Factory reset is not allowed for user."); return false; } return true; } // if BOARD_16K_OTA_MOVE_VENDOR, OTAs will be present on the /vendor partition private File getOtaFile() throws FileNotFoundException { String otaPath = mEnable16k ? OTA_16K_PATH : OTA_4K_PATH; Loading src/com/android/settings/development/Enable16kUtils.java 0 → 100644 +119 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 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.settings.development; import android.content.Context; import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; import android.service.oemlock.OemLockManager; import android.system.Os; import android.system.OsConstants; import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; public class Enable16kUtils { private static final long PAGE_SIZE = Os.sysconf(OsConstants._SC_PAGESIZE); private static final int PAGE_SIZE_16KB = 16 * 1024; @VisibleForTesting static final String DEV_OPTION_PROPERTY = "ro.product.build.16k_page.enabled"; private static final String TAG = "Enable16kUtils"; /** * @param context uses context to retrieve OEM unlock info * @return true if device is OEM unlocked and factory reset is allowed for user. */ public static boolean isDeviceOEMUnlocked(@NonNull Context context) { // OEM unlock is checked for bootloader, carrier and user. Check all three to ensure // that device is unlocked and it is also allowed by user as well as carrier final OemLockManager oemLockManager = context.getSystemService(OemLockManager.class); final UserManager userManager = context.getSystemService(UserManager.class); if (oemLockManager == null || userManager == null) { Log.e(TAG, "Required services not found on device to check for OEM unlock state."); return false; } // If either of device or carrier is not allowed to unlock, return false if (!oemLockManager.isDeviceOemUnlocked()) { Log.e(TAG, "Device is not OEM unlocked"); return false; } final UserHandle userHandle = UserHandle.of(UserHandle.myUserId()); if (userManager.hasBaseUserRestriction(UserManager.DISALLOW_FACTORY_RESET, userHandle)) { Log.e(TAG, "Factory reset is not allowed for user."); return false; } return true; } /** * @return true if /data partition is ext4 */ public static boolean isDataExt4() { try (BufferedReader br = new BufferedReader(new FileReader("/proc/mounts"))) { String line; while ((line = br.readLine()) != null) { Log.i(TAG, line); final String[] fields = line.split(" "); final String partition = fields[1]; final String fsType = fields[2]; if (partition.equals("/data") && fsType.equals("ext4")) { return true; } } } catch (IOException e) { Log.e(TAG, "Failed to read /proc/mounts"); } return false; } /** * @return returns true if 16KB developer option is available for the device. */ public static boolean is16KbToggleAvailable() { return SystemProperties.getBoolean(DEV_OPTION_PROPERTY, false); } /** * 16kB page-agnostic mode requires /data to be ext4, ro.product.build.16k_page.enabled for * device and Device OEM unlocked. * * @param context is needed to query OEM unlock state * @return true if device is in page-agnostic mode. */ public static boolean isPageAgnosticModeOn(@NonNull Context context) { return is16KbToggleAvailable() && isDeviceOEMUnlocked(context) && isDataExt4(); } /** * @return returns true if current page size is 16KB */ public static boolean isUsing16kbPages() { return PAGE_SIZE == PAGE_SIZE_16KB; } } src/com/android/settings/development/PageAgnosticNotificationService.java 0 → 100644 +119 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 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.settings.development; import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; import android.content.Intent; import android.os.IBinder; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.settings.R; public class PageAgnosticNotificationService extends Service { private static final String NOTIFICATION_CHANNEL_ID = "com.android.settings.development.PageAgnosticNotificationService"; private static final int NOTIFICATION_ID = 1; private NotificationManager mNotificationManager; @Nullable @Override public IBinder onBind(@NonNull Intent intent) { return null; } // create a notification channel to post persistent notification private void createNotificationChannel() { NotificationChannel channel = new NotificationChannel( NOTIFICATION_CHANNEL_ID, getString(R.string.page_agnostic_notification_channel_name), NotificationManager.IMPORTANCE_HIGH); mNotificationManager = getSystemService(NotificationManager.class); if (mNotificationManager != null) { mNotificationManager.createNotificationChannel(channel); } } @Override public void onCreate() { super.onCreate(); createNotificationChannel(); } private Notification buildNotification() { // Get the title and text according to page size boolean isIn16kbMode = Enable16kUtils.isUsing16kbPages(); String title = isIn16kbMode ? getString(R.string.page_agnostic_16k_pages_title) : getString(R.string.page_agnostic_4k_pages_title); String text = isIn16kbMode ? getString(R.string.page_agnostic_16k_pages_text_short) : getString(R.string.page_agnostic_4k_pages_text_short); Intent notifyIntent = new Intent(this, PageAgnosticWarningActivity.class); // Set the Activity to start in a new, empty task. notifyIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); // Create the PendingIntent. PendingIntent notifyPendingIntent = PendingIntent.getActivity( this, 0, notifyIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); Notification.Action action = new Notification.Action.Builder( R.drawable.empty_icon, getString(R.string.page_agnostic_notification_action), notifyPendingIntent) .build(); Notification.Builder builder = new Notification.Builder(this, NOTIFICATION_CHANNEL_ID) .setContentTitle(title) .setContentText(text) .setOngoing(true) .setSmallIcon(R.drawable.ic_settings_24dp) .setStyle(new Notification.BigTextStyle().bigText(text)) .setContentIntent(notifyPendingIntent) .addAction(action); return builder.build(); } @Override public int onStartCommand(@Nullable Intent intent, int flags, int startId) { Notification notification = buildNotification(); if (mNotificationManager != null) { mNotificationManager.notify(NOTIFICATION_ID, notification); } // When clicked on notification, show dialog with full text return Service.START_NOT_STICKY; } } Loading
AndroidManifest.xml +22 −0 Original line number Diff line number Diff line Loading @@ -231,6 +231,28 @@ </intent-filter> </receiver> <receiver android:name=".development.Enable16KBootReceiver" android:enabled="true" android:exported="false"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> </intent-filter> </receiver> <service android:name=".development.PageAgnosticNotificationService" android:enabled="true" android:exported="false" android:permission="android.permission.POST_NOTIFICATIONS"/> <activity android:name=".development.PageAgnosticWarningActivity" android:enabled="true" android:launchMode="singleTask" android:taskAffinity="" android:excludeFromRecents="true" android:theme="@*android:style/Theme.DeviceDefault.Dialog.Alert.DayNight"/> <activity android:name=".SubSettings" android:exported="false" android:theme="@style/Theme.SubSettings" Loading
src/com/android/settings/development/Enable16KBootReceiver.java 0 → 100644 +44 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 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.settings.development; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.os.UserHandle; import androidx.annotation.NonNull; public class Enable16KBootReceiver extends BroadcastReceiver { @Override public void onReceive(@NonNull Context context, @NonNull Intent intent) { String action = intent.getAction(); if (!Intent.ACTION_BOOT_COMPLETED.equals(action)) { return; } // Do nothing if device is not in page-agnostic mode if (!Enable16kUtils.isPageAgnosticModeOn(context)) { return; } // start a service to post persistent notification Intent startNotificationIntent = new Intent(context, PageAgnosticNotificationService.class); context.startServiceAsUser(startNotificationIntent, UserHandle.SYSTEM); } }
src/com/android/settings/development/Enable16kPagesPreferenceController.java +6 −65 Original line number Diff line number Diff line Loading @@ -23,17 +23,11 @@ import android.os.ParcelFileDescriptor; import android.os.PersistableBundle; import android.os.PowerManager; import android.os.RecoverySystem; import android.os.SystemProperties; import android.os.SystemUpdateManager; import android.os.UpdateEngine; import android.os.UpdateEngineStable; import android.os.UpdateEngineStableCallback; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; import android.service.oemlock.OemLockManager; import android.system.Os; import android.system.OsConstants; import android.util.Log; import android.widget.LinearLayout; import android.widget.ProgressBar; Loading @@ -59,7 +53,6 @@ import com.google.common.util.concurrent.MoreExecutors; import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; Loading @@ -80,10 +73,6 @@ public class Enable16kPagesPreferenceController extends DeveloperOptionsPreferen private static final String TAG = "Enable16kPages"; private static final String REBOOT_REASON = "toggle16k"; private static final String ENABLE_16K_PAGES = "enable_16k_pages"; @VisibleForTesting static final String DEV_OPTION_PROPERTY = "ro.product.build.16k_page.enabled"; private static final int ENABLE_4K_PAGE_SIZE = 0; private static final int ENABLE_16K_PAGE_SIZE = 1; Loading @@ -97,9 +86,6 @@ public class Enable16kPagesPreferenceController extends DeveloperOptionsPreferen private static final int OFFSET_TO_FILE_NAME = 30; public static final String EXPERIMENTAL_UPDATE_TITLE = "Android 16K Kernel Experimental Update"; private static final long PAGE_SIZE = Os.sysconf(OsConstants._SC_PAGESIZE); private static final int PAGE_SIZE_16KB = 16 * 1024; private @NonNull DevelopmentSettingsDashboardFragment mFragment; private boolean mEnable16k; Loading @@ -112,12 +98,12 @@ public class Enable16kPagesPreferenceController extends DeveloperOptionsPreferen @NonNull Context context, @NonNull DevelopmentSettingsDashboardFragment fragment) { super(context); this.mFragment = fragment; mEnable16k = (PAGE_SIZE == PAGE_SIZE_16KB); mEnable16k = Enable16kUtils.isUsing16kbPages(); } @Override public boolean isAvailable() { return SystemProperties.getBoolean(DEV_OPTION_PROPERTY, false); return Enable16kUtils.is16KbToggleAvailable(); } @Override Loading @@ -129,12 +115,12 @@ public class Enable16kPagesPreferenceController extends DeveloperOptionsPreferen public boolean onPreferenceChange(Preference preference, Object newValue) { mEnable16k = (Boolean) newValue; // Prompt user to do oem unlock first if (!isDeviceOEMUnlocked()) { if (!Enable16kUtils.isDeviceOEMUnlocked(mContext)) { Enable16KOemUnlockDialog.show(mFragment); return false; } if (isDataf2fs()) { if (!Enable16kUtils.isDataExt4()) { EnableExt4WarningDialog.show(mFragment, this); return false; } Loading @@ -145,7 +131,7 @@ public class Enable16kPagesPreferenceController extends DeveloperOptionsPreferen @Override public void updateState(Preference preference) { int defaultOptionValue = PAGE_SIZE == PAGE_SIZE_16KB ? ENABLE_16K_PAGE_SIZE : ENABLE_4K_PAGE_SIZE; Enable16kUtils.isUsing16kbPages() ? ENABLE_16K_PAGE_SIZE : ENABLE_4K_PAGE_SIZE; final int optionValue = Settings.Global.getInt( mContext.getContentResolver(), Loading @@ -169,7 +155,7 @@ public class Enable16kPagesPreferenceController extends DeveloperOptionsPreferen @Override protected void onDeveloperOptionsSwitchEnabled() { int currentStatus = PAGE_SIZE == PAGE_SIZE_16KB ? ENABLE_16K_PAGE_SIZE : ENABLE_4K_PAGE_SIZE; Enable16kUtils.isUsing16kbPages() ? ENABLE_16K_PAGE_SIZE : ENABLE_4K_PAGE_SIZE; Settings.Global.putInt( mContext.getContentResolver(), Settings.Global.ENABLE_16K_PAGES, currentStatus); } Loading Loading @@ -432,51 +418,6 @@ public class Enable16kPagesPreferenceController extends DeveloperOptionsPreferen return infoBundle; } private boolean isDataf2fs() { try (BufferedReader br = new BufferedReader(new FileReader("/proc/mounts"))) { String line; while ((line = br.readLine()) != null) { final String[] fields = line.split(" "); final String partition = fields[1]; final String fsType = fields[2]; if (partition.equals("/data") && fsType.equals("f2fs")) { return true; } } } catch (IOException e) { Log.e(TAG, "Failed to read /proc/mounts"); displayToast(mContext.getString(R.string.format_ext4_failure_toast)); } return false; } private boolean isDeviceOEMUnlocked() { // OEM unlock is checked for bootloader, carrier and user. Check all three to ensure // that device is unlocked and it is also allowed by user as well as carrier final OemLockManager oemLockManager = mContext.getSystemService(OemLockManager.class); final UserManager userManager = mContext.getSystemService(UserManager.class); if (oemLockManager == null || userManager == null) { Log.e(TAG, "Required services not found on device to check for OEM unlock state."); return false; } // If either of device or carrier is not allowed to unlock, return false if (!oemLockManager.isDeviceOemUnlocked() || !oemLockManager.isOemUnlockAllowedByCarrier()) { Log.e(TAG, "Device is not OEM unlocked or it is not allowed by carrier"); return false; } final UserHandle userHandle = UserHandle.of(UserHandle.myUserId()); if (userManager.hasBaseUserRestriction(UserManager.DISALLOW_FACTORY_RESET, userHandle)) { Log.e(TAG, "Factory reset is not allowed for user."); return false; } return true; } // if BOARD_16K_OTA_MOVE_VENDOR, OTAs will be present on the /vendor partition private File getOtaFile() throws FileNotFoundException { String otaPath = mEnable16k ? OTA_16K_PATH : OTA_4K_PATH; Loading
src/com/android/settings/development/Enable16kUtils.java 0 → 100644 +119 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 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.settings.development; import android.content.Context; import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; import android.service.oemlock.OemLockManager; import android.system.Os; import android.system.OsConstants; import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; public class Enable16kUtils { private static final long PAGE_SIZE = Os.sysconf(OsConstants._SC_PAGESIZE); private static final int PAGE_SIZE_16KB = 16 * 1024; @VisibleForTesting static final String DEV_OPTION_PROPERTY = "ro.product.build.16k_page.enabled"; private static final String TAG = "Enable16kUtils"; /** * @param context uses context to retrieve OEM unlock info * @return true if device is OEM unlocked and factory reset is allowed for user. */ public static boolean isDeviceOEMUnlocked(@NonNull Context context) { // OEM unlock is checked for bootloader, carrier and user. Check all three to ensure // that device is unlocked and it is also allowed by user as well as carrier final OemLockManager oemLockManager = context.getSystemService(OemLockManager.class); final UserManager userManager = context.getSystemService(UserManager.class); if (oemLockManager == null || userManager == null) { Log.e(TAG, "Required services not found on device to check for OEM unlock state."); return false; } // If either of device or carrier is not allowed to unlock, return false if (!oemLockManager.isDeviceOemUnlocked()) { Log.e(TAG, "Device is not OEM unlocked"); return false; } final UserHandle userHandle = UserHandle.of(UserHandle.myUserId()); if (userManager.hasBaseUserRestriction(UserManager.DISALLOW_FACTORY_RESET, userHandle)) { Log.e(TAG, "Factory reset is not allowed for user."); return false; } return true; } /** * @return true if /data partition is ext4 */ public static boolean isDataExt4() { try (BufferedReader br = new BufferedReader(new FileReader("/proc/mounts"))) { String line; while ((line = br.readLine()) != null) { Log.i(TAG, line); final String[] fields = line.split(" "); final String partition = fields[1]; final String fsType = fields[2]; if (partition.equals("/data") && fsType.equals("ext4")) { return true; } } } catch (IOException e) { Log.e(TAG, "Failed to read /proc/mounts"); } return false; } /** * @return returns true if 16KB developer option is available for the device. */ public static boolean is16KbToggleAvailable() { return SystemProperties.getBoolean(DEV_OPTION_PROPERTY, false); } /** * 16kB page-agnostic mode requires /data to be ext4, ro.product.build.16k_page.enabled for * device and Device OEM unlocked. * * @param context is needed to query OEM unlock state * @return true if device is in page-agnostic mode. */ public static boolean isPageAgnosticModeOn(@NonNull Context context) { return is16KbToggleAvailable() && isDeviceOEMUnlocked(context) && isDataExt4(); } /** * @return returns true if current page size is 16KB */ public static boolean isUsing16kbPages() { return PAGE_SIZE == PAGE_SIZE_16KB; } }
src/com/android/settings/development/PageAgnosticNotificationService.java 0 → 100644 +119 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 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.settings.development; import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; import android.content.Intent; import android.os.IBinder; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.settings.R; public class PageAgnosticNotificationService extends Service { private static final String NOTIFICATION_CHANNEL_ID = "com.android.settings.development.PageAgnosticNotificationService"; private static final int NOTIFICATION_ID = 1; private NotificationManager mNotificationManager; @Nullable @Override public IBinder onBind(@NonNull Intent intent) { return null; } // create a notification channel to post persistent notification private void createNotificationChannel() { NotificationChannel channel = new NotificationChannel( NOTIFICATION_CHANNEL_ID, getString(R.string.page_agnostic_notification_channel_name), NotificationManager.IMPORTANCE_HIGH); mNotificationManager = getSystemService(NotificationManager.class); if (mNotificationManager != null) { mNotificationManager.createNotificationChannel(channel); } } @Override public void onCreate() { super.onCreate(); createNotificationChannel(); } private Notification buildNotification() { // Get the title and text according to page size boolean isIn16kbMode = Enable16kUtils.isUsing16kbPages(); String title = isIn16kbMode ? getString(R.string.page_agnostic_16k_pages_title) : getString(R.string.page_agnostic_4k_pages_title); String text = isIn16kbMode ? getString(R.string.page_agnostic_16k_pages_text_short) : getString(R.string.page_agnostic_4k_pages_text_short); Intent notifyIntent = new Intent(this, PageAgnosticWarningActivity.class); // Set the Activity to start in a new, empty task. notifyIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); // Create the PendingIntent. PendingIntent notifyPendingIntent = PendingIntent.getActivity( this, 0, notifyIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); Notification.Action action = new Notification.Action.Builder( R.drawable.empty_icon, getString(R.string.page_agnostic_notification_action), notifyPendingIntent) .build(); Notification.Builder builder = new Notification.Builder(this, NOTIFICATION_CHANNEL_ID) .setContentTitle(title) .setContentText(text) .setOngoing(true) .setSmallIcon(R.drawable.ic_settings_24dp) .setStyle(new Notification.BigTextStyle().bigText(text)) .setContentIntent(notifyPendingIntent) .addAction(action); return builder.build(); } @Override public int onStartCommand(@Nullable Intent intent, int flags, int startId) { Notification notification = buildNotification(); if (mNotificationManager != null) { mNotificationManager.notify(NOTIFICATION_ID, notification); } // When clicked on notification, show dialog with full text return Service.START_NOT_STICKY; } }