提示:MTK平台-去除第一次开机-默认权限提示框
文章目录
- 前言-需求
- 一、修改文件
- 二、实现方案
- 三、知识点梳理
-
- [当前需求PermissionsAnnouncement 相关类](#当前需求PermissionsAnnouncement 相关类)
- [为什么会弹出这个提醒框-M: CTA requirement - permission control](#为什么会弹出这个提醒框-M: CTA requirement - permission control)
- [服务PermissionsAnnouncementService 源码分析](#服务PermissionsAnnouncementService 源码分析)
- UI界面PermissionsAnnouncementActivity源码分析
- UI界面PermissionsAnnouncementFragment源码分析
- 解决方案小结
- [四、服务监听的广播-ACTION_USER_UNLOCKED 分析](#四、服务监听的广播-ACTION_USER_UNLOCKED 分析)
- 总结
前言-需求
如下,要去掉这一个界面:从用户可感知的场景是刷完固件、恢复出厂设置后第一次开机后的一个提醒页面,如下

为什么要去掉这个界面-需求原因
自己总结主要有两点:
- 这个界面本身是 MTK Android平台独有的,一个提醒而已,大部分客户反馈没什么存在的必要性。也就是说开机后直接进入系统,不要搞一个提醒页面。
- 在部分项目中发现,预制客户第三方App 作为系统Launcher,本身应该先提醒页面的,发现实际发现每次开机提醒页面弹出来迅速消失,才直接显示应用Launcher Home 程序。
从解决需求的角度来说,直接把提醒页面去掉为最优解!
一、修改文件
java
/vendor/mediatek/proprietary/packages/apps/PermissionController/AndroidManifest.xml
二、实现方案
屏蔽掉如下代码:
java
<!-- M: CTA requirement - permission control -->
<!-- <activity
android:name="com.mediatek.permissioncontroller.PermissionsAnnouncementActivity"
android:configChanges="keyboardHidden"
android:theme="@style/NoActionBar"></activity>-->
<!-- M: CTA requirement - permission control -->
<!-- <service
android:name="com.mediatek.permissioncontroller.PermissionsAnnouncementService"
android:exported="false">
<intent-filter>
<action android:name="com.mediatek.permission.PERMISSIONS_ANNOUNCEMENT"></action>
</intent-filter>
</service> -->
其实主要屏蔽掉 服务 PermissionsAnnouncementService 即可。

三、知识点梳理
当前需求PermissionsAnnouncement 相关类
如下,直接看下关联的几个类:

为什么会弹出这个提醒框-M: CTA requirement - permission control
java
<!-- M: CTA requirement - permission control -->
<service
android:name="com.mediatek.permissioncontroller.PermissionsAnnouncementService"
android:exported="false">
<intent-filter>
<action android:name="com.mediatek.permission.PERMISSIONS_ANNOUNCEMENT"></action>
</intent-filter>
</service>
看注释: CTA requirement - permission control
CTA = China Type Approval(中国型号核准 / 工信部合规要求)- 意思:这是为了满足中国工信部对安卓手机权限管理的合规强制要求才加的服务。
所以这个服务在实际非CTA 要求下的项目是没有任何存在的必要,也是MTK独有的一套。
服务PermissionsAnnouncementService 源码分析
这接看源码,如下:
分析:其实这个服务源码相当简单,就是通过一个广播拉起,然后判断当前有没有设置过,如果没有设置过就弹出一个界面。
这不就是 第一次弹框,如果设置一次就不会再弹框了嘛。
java
package com.mediatek.permissioncontroller;
.................................
public class PermissionsAnnouncementService extends Service {
private static final String TAG = PermissionsAnnouncementService.class.getSimpleName();
private static final String NOTIFICATION_CHANNEL = "com.mediatek.permissioncontroller";
private static final CharSequence NOTIFICATION_NAME = "Announcement permissions";
private NotificationChannel mChannel;
private boolean mRegisteredReceiver = false;
private BroadcastReceiver mUserUnlockedReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (Intent.ACTION_USER_UNLOCKED.equals(intent.getAction())) {
startPermissionAnnouncementActivity();
stopForeground(true);
}
}
};
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate");
mChannel = new NotificationChannel(NOTIFICATION_CHANNEL, NOTIFICATION_NAME,
NotificationManager.IMPORTANCE_LOW);
if (!mRegisteredReceiver) {
registerReceiver(mUserUnlockedReceiver, new IntentFilter(Intent.ACTION_USER_UNLOCKED));
mRegisteredReceiver = true;
}
}
@Override
public int onStartCommand(Intent intent, int flag, int startId) {
Log.d(TAG, "onStartCommand");
NotificationManager mNotificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.createNotificationChannel(mChannel);
Notification notification = new Notification.Builder(getApplicationContext())
.setChannelId(NOTIFICATION_CHANNEL)
.build();
startForeground(1, notification);
return super.onStartCommand(intent, flag, startId);
}
@Override
public IBinder onBind(Intent intent) {
return new Binder();
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy");
unregisterReceiver(mUserUnlockedReceiver);
}
private void startPermissionAnnouncementActivity() {
SharedPreferences mySharedPreferences = getSharedPreferences("config", MODE_PRIVATE);
int hasSet = mySharedPreferences.getInt("start", 1);
Log.d(TAG, "hasSet = " + hasSet);
if (hasSet == 1) {
Intent intent = new Intent();
intent.setPackage(getPackageName());
intent.setClass(this, PermissionsAnnouncementActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
} else {
stopForeground(true);
}
}
}
UI界面PermissionsAnnouncementActivity源码分析
直接查看源码,如下: 其实就是包裹了Fragment,没有什么需要注意的地方
java
package com.mediatek.permissioncontroller;
import androidx.fragment.app.FragmentActivity;
import android.os.Bundle;
import android.util.Log;
import android.app.StatusBarManager;
import android.content.Context;
import android.widget.TextView;
import android.content.Intent;
import android.os.UserHandle;
import android.content.res.Configuration;
import com.android.permissioncontroller.R;
public class PermissionsAnnouncementActivity extends FragmentActivity {
private static final String TAG = PermissionsAnnouncementActivity.class.getSimpleName();
private boolean mStatusBarDisabled = false;
private TextView mTitle;
@Override
protected void onCreate(Bundle savedInstanceState) {
Log.i(TAG, "onCreate");
super.onCreate(savedInstanceState);
setContentView(R.layout.permissions_announcement_main_layout);
if (getSupportFragmentManager().findFragmentById(R.id.preferences_frame) == null) {
getSupportFragmentManager().beginTransaction().add(R.id.preferences_frame,
new PermissionsAnnouncementFragment()).commit();
}
}
@Override
protected void onResume() {
Log.i(TAG, "onResume");
super.onResume();
if (!mStatusBarDisabled) {
setStatusBarEnableStatus(false);
mStatusBarDisabled = true;
}
}
@Override
public void onConfigurationChanged(Configuration config) {
super.onConfigurationChanged(config);
Log.i(TAG, "onConfigurationChanged");
}
@Override
public void onPause() {
Log.i(TAG, "onPause");
super.onPause();
if (mStatusBarDisabled) {
setStatusBarEnableStatus(true);
mStatusBarDisabled = false;
}
}
@Override
public void onDestroy() {
Log.i(TAG, "onDestroy");
super.onDestroy();
if (mStatusBarDisabled) {
setStatusBarEnableStatus(true);
mStatusBarDisabled = false;
}
Intent intent = new Intent();
intent.setClassName("com.android.permissioncontroller",
"com.mediatek.permissioncontroller.PermissionsAnnouncementService");
stopServiceAsUser(intent, UserHandle.SYSTEM);
}
private void setStatusBarEnableStatus(boolean enabled) {
Log.i(TAG, "setStatusBarEnableStatus(" + enabled + ")");
StatusBarManager statusBarManager;
statusBarManager = (StatusBarManager) getSystemService(Context.STATUS_BAR_SERVICE);
if (statusBarManager != null) {
if (enabled) {
statusBarManager.disable(StatusBarManager.DISABLE_NONE);
} else {
statusBarManager.disable(StatusBarManager.DISABLE_EXPAND
| StatusBarManager.DISABLE_RECENT
| StatusBarManager.DISABLE_BACK
| StatusBarManager.DISABLE_HOME);
}
} else {
Log.e(TAG, "Fail to get status bar instance");
}
}
}
UI界面PermissionsAnnouncementFragment源码分析
直接看源码:
这里注意两点:
- 权限展示
loadPreferences方法,把所有权限都展示处理:String[] permissionArray = mContext.getResources().getStringArray(R.array.packages_permissions) - 确定按钮,点击:
editor.putInt("start", 0);不就是上面服务PermissionsAnnouncementService中 是否
java
package com.mediatek.permissioncontroller;
import android.app.Activity;
import android.os.Bundle;
import android.os.IBinder;
import android.os.UserHandle;
import android.view.View;
import android.widget.Button;
import android.util.Log;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.ServiceConnection;
import android.content.ComponentName;
import android.graphics.drawable.Drawable;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.SharedPreferences;
import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceGroup;
import androidx.preference.PreferenceScreen;
import com.android.permissioncontroller.R;
public class PermissionsAnnouncementFragment extends PreferenceFragmentCompat
implements View.OnClickListener {
private Context mContext;
private Button mButtonConfirm;
private static final String LOG_TAG = PermissionsAnnouncementFragment.class.getSimpleName();
private boolean mShouldUnbind = false;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i(LOG_TAG, "onCreate");
Activity activity = getActivity();
if (activity == null) {
return;
}
}
@Override
public void onCreatePreferences(Bundle bundle, String s) {
// empty
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
bindUi();
}
@Override
public void onResume() {
Log.i(LOG_TAG, "onResume");
super.onResume();
loadPreferences();
}
@Override
public void onClick(View view) {
Log.d(LOG_TAG, "onClick");
Activity activity = getActivity();
if (activity == null) {
return;
}
if (view == mButtonConfirm) {
SharedPreferences mySharedPreferences = mContext.getSharedPreferences("config",
Context.MODE_PRIVATE);
SharedPreferences.Editor editor = mySharedPreferences.edit();
editor.putInt("start", 0);
editor.commit();
Log.d(LOG_TAG, "afterhasSet = " + mySharedPreferences.getInt("start", 1));
activity.finish();
}
}
private void loadPreferences() {
mContext = getContext();
Activity activity = getActivity();
if (activity == null) {
return;
}
PreferenceScreen screen = getPreferenceScreen();
if (screen == null) {
screen = getPreferenceManager().createPreferenceScreen(getContext());
setPreferenceScreen(screen);
} else {
screen.removeAll();
}
String[] permissionArray = mContext.getResources().getStringArray(R.array.packages_permissions);
for (String permstr : permissionArray) {
String[] splitPermResult = permstr.split("\\|", 2);
PreferenceCategory category = new PreferenceCategory(activity);
String categoryTitle = getString(R.string.permissions_announcement_title,
splitPermResult[1]);
category.setTitle(categoryTitle);
screen.addPreference(category);
int nameId = mContext.getResources().getIdentifier(splitPermResult[0] + "_names",
"array", mContext.getPackageName());
int summaryId = mContext.getResources().getIdentifier(splitPermResult[0] + "_summary",
"array", mContext.getPackageName());
String[] packNameArray = mContext.getResources().getStringArray(nameId);
String[] packSummaryArray = mContext.getResources().getStringArray(summaryId);
int i = 0;
for (String packstr : packNameArray) {
Log.d(LOG_TAG, "permstr: " + permstr + " ,packstr: " + packstr);
Preference preference = new Preference(getContext());
Drawable icon = getApplicationIcon(mContext, packstr);
preference.setTitle(packstr);
preference.setIcon(icon);
preference.setSummary(packSummaryArray[i]);
category.addPreference(preference);
i++;
}
}
}
private void bindUi() {
Activity activity = getActivity();
if (activity == null) {
return;
}
mButtonConfirm = (Button )getActivity().findViewById(R.id.confirm);
mButtonConfirm.setOnClickListener(this);
}
private static Drawable getApplicationIcon(Context context, String pkgName) {
PackageManager pm = context.getPackageManager();
Drawable icon = null;
try {
icon = pm.getApplicationIcon(pkgName);
//need test if pass pkgName can get icon
} catch (NameNotFoundException ex) {
icon = pm.getDefaultActivityIcon();
Log.e(LOG_TAG, pkgName + " app icon not found, using generic app icon");
}
return icon;
}
}
解决方案小结
如上源码分析了以后,其实解决方案就很多了,目标就是不让这个权限提醒界面弹出来。
- 在配置文件中屏蔽服务
PermissionsAnnouncementService,不让启动 - 在服务
PermissionsAnnouncementService中去掉广播ACTION_USER_UNLOCKED的监听 - 在广播触发里面,判断是否启动权限提醒UI界面,拦截掉
- 直接把UI界面在配置文件中屏蔽
- 默认sp 值。
四、服务监听的广播-ACTION_USER_UNLOCKED 分析
这个其实就是锁屏、解锁 相关业务发送出来的,这里重点知道发送的场景。 比如在手机、平板相关产品非工控行业产品,那么锁屏、PIN码机制是存在的,那么就务必重点分析 这个广播。
这里总结的主要解决权限弹框问题,知道如何屏蔽即可。
总结
这里我们规整知识点,主要有三个知识点务必掌握
- 这个界面的来源,只有MTK平台存在
- 知晓如何屏蔽不让显示
- 知晓为什么要屏蔽:1)体验感官上需要屏蔽 2) 部分项目这个界面导致每次开机闪一下界面UI,导致显示bug,必须去掉。