【问题标题】:如何以编程方式检查 MIUI 自动启动权限?
【发布时间】:2017-01-14 22:16:26
【问题描述】:

我需要以编程方式检查我的应用在 MIUI 手机中的自动启动权限是打开还是关闭。 Facebook 和 whatsapp 已经默认开启了这个权限,我该怎么做呢?

【问题讨论】:

  • 距离您提出这个问题已有 5 年了。现在有更好的办法吗?

标签: android android-permissions background-service autostart redmi-device


【解决方案1】:

您可以使用此库检查 MIUI 10、11 和 12 上的自动启动权限状态。

https://github.com/XomaDev/MIUI-autostart

// make sure device is MIUI device, else an 
// exception will be thrown at initialization
Autostart autostart = new Autostart(applicationContext);

State state = autostart.getAutoStartState();

if (state == State.DISABLED) {
    // now we are sure that autostart is disabled
    // ask user to enable it manually in the settings app    
} else if (state == State.ENABLED) {
    // now we are also sure that autostart is enabled
}

【讨论】:

    【解决方案2】:

    这段代码对我有用。简单易行。 Credit

      private State getAutoStartState(Activity activity) throws Exception {
        Class<?> clazz;
        try {
            clazz = Class.forName(CLAZZ);
        } catch (ClassNotFoundException ignored) {
            // we don't know if its enabled, class
            // is not found, no info
            return State.NO_INFO;
        }
        final Method method = getMethod(clazz);
        if (method == null) {
            // exception raised while search the method,
            // or it doesn't exist
            return State.NO_INFO;
        }
        // the method is a public method, It's still
        // better to do this
        method.setAccessible(true);
    
        // the target object is null, because the
        // method is static
        final Object result = method.invoke(null, getActivity(),
                getActivity().getPackageName());
    
        // the result should be an Int
        if (!(result instanceof Integer))
            throw new Exception();
    
        final int _int = (int) result;
    
        if (_int == ENABLED)
            return State.ENABLED;
        else if (_int == DISABLED)
            return State.DISABLED;
        return State.UNKNOWN;
    }
    
    private Method getMethod(Class<?> clazz) {
        try {
            return clazz.getDeclaredMethod("getApplicationAutoStart",
                    Context.class, String.class);
        } catch (Exception ignored) {
            // this should not happen, probably
            // MIUI version is updated, lets give a last try
            return null;
        }
    }
    public void checkMIUIAutoStart(Activity activity) throws Exception {
        if (getAutoStartState(activity) == State.DISABLED) {
    
            String manufacturer = "xiaomi";
            if (manufacturer.equalsIgnoreCase(android.os.Build.MANUFACTURER)) {
                //this will open auto start screen where user can enable permission for your app
                Intent intent1 = new Intent();
                intent1.setComponent(new ComponentName("com.miui.securitycenter", "com.miui.permcenter.autostart.AutoStartManagementActivity"));
                startActivity(intent1);
            }
    
        }else {
            Toast.makeText(activity, "Auto-start is enabled.", Toast.LENGTH_SHORT).show();
        }
    }
    

    【讨论】:

      【解决方案3】:

      为了检查是否启用了权限,我只是启动了一个前台服务并检查是否正在运行。

      服务:

      class ExtraPermissionStateService: Service() {
      
          companion object {
              private var instance: ExtraPermissionStateService? = null
      
              fun isAppCanRunOnBackground(context: Context): Boolean {
                  val serviceIntent = Intent(context, ExtraPermissionStateService::class.java)
                  context.startService(serviceIntent)
                  return instance != null
              }
          }
      
          override fun onBind(p0: Intent?): IBinder? {
              return null
          }
      
          override fun onCreate() {
              super.onCreate()
              instance = this
          }
      
          override fun onDestroy() {
              super.onDestroy()
              instance = null
          }
      }
      

      叫它:

      ExtraPermissionStateService.isAppCanRunOnBackground(context)

      不要忘记清单:

      &lt;service android:name=".helpers.utils.ExtraPermissionStateService"/&gt;

      【讨论】:

      • 这没有回答问题,因为自动启动不是权限,而是定制的 OEM/OS 提供的功能,并且您的代码没有任何声明来检查权限。
      【解决方案4】:

      您无法检查是否启用了自动运行权限,因为自动运行功能仅由自定义操作系统提供,而不是由 mi、vivo、oppo、letv 等 android 操作系统提供

      这是在 MI、Honor 和 vivo 手机上测试的解决方法。

      要检查os是否像miui一样自定义,请将此方法复制粘贴到activity、fragment或util类中

      public static void getAutoStartPermission(final Activity context) {
              final String build_info = Build.BRAND.toLowerCase();
              switch (build_info) {
                  case "xiaomi":
                      Utilities.Companion.showAutorunDialog(context);
                      break;
                  case "letv":
                      Utilities.Companion.showAutorunDialog(context);
                      break;
                  case "oppo":
                      Utilities.Companion.showAutorunDialog(context);
                      break;
                  case "vivo":
                      Utilities.Companion.showAutorunDialog(context);
                      break;
                  case "Honor":
                      Utilities.Companion.showAutorunDialog(context);
                      break;
                  default:
                      break;
      
              }
      
          }
      

      在哪里

      fun showAutorunDialog(context: Context) {
                  val builder = AlertDialog.Builder(context)
                  //set title for alert dialog
                  builder.setTitle("Alert")
                  //set message for alert dialog
                  builder.setMessage("Enable Autostart permission for this app if its disabled in app settings in order to run application in background.")
                  builder.setCancelable(true)
                  //performing positive action
                  builder.setPositiveButton("Enable") { _, _ ->
                      addAutoStartup(context)
                  }
      
                  // Create the AlertDialog
                    var  vpnDialog = builder.create()
                      // Set other dialog properties
                      vpnDialog!!.setCancelable(false)
                      vpnDialog!!.show()
      
              }
              private fun addAutoStartup(context:Context) {
                  try {
                      val intent = Intent()
                      val manufacturer = Build.MANUFACTURER
                      if ("xiaomi".equals(manufacturer, ignoreCase = true)) {
                          intent.component = ComponentName("com.miui.securitycenter", "com.miui.permcenter.autostart.AutoStartManagementActivity")
                      } else if ("oppo".equals(manufacturer, ignoreCase = true)) {
                          intent.component = ComponentName("com.coloros.safecenter", "com.coloros.safecenter.permission.startup.StartupAppListActivity")
                      } else if ("vivo".equals(manufacturer, ignoreCase = true)) {
                          intent.component = ComponentName("com.vivo.permissionmanager", "com.vivo.permissionmanager.activity.BgStartUpManagerActivity")
                      } else if ("Letv".equals(manufacturer, ignoreCase = true)) {
                          intent.component = ComponentName("com.letv.android.letvsafe", "com.letv.android.letvsafe.AutobootManageActivity")
                      } else if ("Honor".equals(manufacturer, ignoreCase = true)) {
                          intent.component = ComponentName("com.huawei.systemmanager", "com.huawei.systemmanager.optimize.process.ProtectActivity")
                      }
                      val list: List<ResolveInfo> = context.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY)
                      if (list.size > 0) {
                          context.startActivity(intent)
                      }
                  } catch (e: java.lang.Exception) {
                      Log.e("exc", e.toString())
                  }
              }
      

      【讨论】:

        【解决方案5】:

        目前这是不可能的。 因为它完全取决于他们的操作系统 API 和定制。但我使用 SharedPreference 实现了修复。它没有解决问题,但它会阻止应用程序在每次打开应用程序时打开设置屏幕。示例:

         if (AppPref.getAutoStart(context).isEmpty() && AppPref.getAutoStart(context).equals("")) {
                enableAutoStart();
            }
        
        private void enableAutoStart() {
            if (Build.BRAND.equalsIgnoreCase("xiaomi")) {
        
                new AlertDialog.Builder(context)
                        .setTitle("Enable AutoStart")
                        .setMessage("Please allow this app to always run in the background,else our services can't be accessed.")
                        .setNegativeButton("Deny", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                AppPref.setAutoStart(context, "");
                                dialog.dismiss();
                            }
                        })
                        .setPositiveButton("ALLOW", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                try {
                                    AppPref.setAutoStart(context, "1");
                                    Intent intent = new Intent();
                                    intent.setComponent(new ComponentName("com.miui.securitycenter",
                                            "com.miui.permcenter.autostart.AutoStartManagementActivity"));
                                    startActivity(intent);
                                } catch (Exception e) {
                                    Toast.makeText(context, "Can't perform action", Toast.LENGTH_SHORT).show();
                                }
                                dialog.dismiss();
                            }
                        })
                        .create()
                        .show();
            }
        }
        

        【讨论】:

          【解决方案6】:

          目前不可能。

          因为它完全取决于他们的操作系统 API 和定制。甚至开发者在小米官方论坛上也提出了这个要求,但那里没有任何回应。

          到目前为止,即使我找到了这个问题的答案,但没有任何帮助。

          目前它仅适用于有根手机。即通过成为超级用户来定制他们的固件。 但这一点也不可取,因为它可能会损坏用户的手机

          编辑 1

          您可以使用以下代码将用户重定向到自动启动权限的设置页面以启用您的应用

          String manufacturer = "xiaomi";
          if (manufacturer.equalsIgnoreCase(android.os.Build.MANUFACTURER)) {
              //this will open auto start screen where user can enable permission for your app
              Intent intent1 = new Intent();
              intent1.setComponent(new ComponentName("com.miui.securitycenter", "com.miui.permcenter.autostart.AutoStartManagementActivity"));
              startActivity(intent1);
          }
          

          编辑 2 我最近使用了来自 XIOMI 的 Mi A1,它有股票 android(不是 miui)所以这款手机没有来自 miui 的autostart permission 设置。因此,在将用户导航到此类设备中的设置时要小心,因为它在这里不起作用。

          【讨论】:

          • 但一些应用程序,如 facebook messenger、whatsapp 等,默认有自动启动选项。知道如何完成吗?
          • @arul 这些应用被 miui os 列入白名单,因此它们会自动获得自动启动权限
          • 是的@indramurari。作为一种解决方法,我们可以要求用户重定向到此处提到的安全应用程序。 stackoverflow.com/questions/40814126/…
          • 到目前为止,这似乎是唯一的解决方法。当自定义 ROM 以这种方式干预时,我讨厌它!
          • @Nikhil 如何检查自动启动权限是否已启用
          【解决方案7】:

          这无论如何都不是一个完美的解决方案,它需要一些测试,但我已经能够在我的小米设备上检测到自动启动权限。

          自动启动权限允许通过接收隐式广播意图来启动应用程序。此方法包括使用 AlarmManager 安排隐式广播、终止应用程序并检查广播是否导致它重生。还安排了第二个显式意图,以确保最终启动应用程序。

          public class AutostartDetector extends BroadcastReceiver {
          
          // I've omitted all the constant declaration to keep this snippet concise
          // they should match the values used in the Manifest
          
          public static void testAutoStart(Context context) {
              long now = System.currentTimeMillis();
              // this ID is for matching the implicit and explicit intents
              // it might be unnecessary
              String testId = Long.toHexString(now);
          
              Intent implicitIntent = new Intent(ACTION_IMPLICIT_BROADCAST);
              // the category is set just to make sure that no other receivers handle the broadcast
              implicitIntent.addCategory(CATEGORY_AUTOSTART);
              implicitIntent.putExtra(EXTRA_TEST_ID, testId);
          
              PendingIntent implicitPendingIntent =
                      PendingIntent.getBroadcast(context, REQUEST_CODE_IMPLICIT_BROADCAST, implicitIntent, PendingIntent.FLAG_UPDATE_CURRENT);
          
              Intent explicitIntent = new Intent(ACTION_EXPLICIT_BROADCAST);
              explicitIntent.addCategory(CATEGORY_AUTOSTART);
              explicitIntent.setComponent(new ComponentName(context, AutostartDetector.class));
              explicitIntent.putExtra(EXTRA_TEST_ID, testId);
          
              PendingIntent explicitPendingIntent =
                      PendingIntent.getBroadcast(context, REQUEST_CODE_EXPLICIT_BROADCAST, explicitIntent, PendingIntent.FLAG_UPDATE_CURRENT);
          
              AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
          
              // calling commit() makes sure that the data is written before we kill the app
              // again, this might be unnecessary
              getSharedPreferences(context).edit().putInt(testId, TestStatus.STARTED).commit();
          
              // the explicit intent is set with an additional delay to let the implicit one be received first; might require some fine tuning
              alarmManager.set(AlarmManager.RTC_WAKEUP, now + BASE_DELAY, implicitPendingIntent);
              alarmManager.set(AlarmManager.RTC_WAKEUP, now + BASE_DELAY + EXPLICIT_INTENT_DELAY, explicitPendingIntent);
          
              // kill the app - actually kind of tricky, see below
              SelfKiller.killSelf(context);
          }
          
          @Override
          public void onReceive(Context context, Intent intent) {
              SharedPreferences sharedPreferences = getSharedPreferences(context);
              String testId = intent.getStringExtra(EXTRA_TEST_ID);
          
              if (testId == null) {
                  Log.w(TAG, "Null test ID");
                  return;
              }
          
              if (!sharedPreferences.contains(testId)) {
                  Log.w(TAG, "Unknown test ID: " + testId);
                  return;
              }
          
              String action = intent.getAction();
              if (ACTION_IMPLICIT_BROADCAST.equals(action)) {
                  // we could assume right here that the autostart permission has been granted,
                  // but we should receive the explicit intent anyway, so let's use it
                  // as a test sanity check
                  Log.v(TAG, "Received implicit broadcast");
                  sharedPreferences.edit().putInt(testId, TestStatus.IMPLICIT_INTENT_RECEIVED).apply();
              } else if (ACTION_EXPLICIT_BROADCAST.equals(action)) {
                  Log.v(TAG, "Received explicit broadcast");
                  int testStatus = sharedPreferences.getInt(testId, -1);
                  switch (testStatus) {
                      case TestStatus.STARTED:
                          // the implicit broadcast has NOT been received - autostart permission denied
                          Log.d(TAG, "Autostart disabled");
                          sharedPreferences.edit().putBoolean(PREF_AUTOSTART_ENABLED, false).apply();
                          notifyListener(false);
                          break;
          
                      case TestStatus.IMPLICIT_INTENT_RECEIVED:
                          // the implicit broadcast has been received - autostart permission granted
                          Log.d(TAG, "Autostart enabled");
                          sharedPreferences.edit().putBoolean(PREF_AUTOSTART_ENABLED, true).apply();
                          notifyListener(true);
                          break;
          
                      default:
                          Log.w(TAG, "Invalid test status: " + testId + ' ' + testStatus);
                          break;
                  }
              }
          }
          
          private interface TestStatus {
              int STARTED = 1;
              int IMPLICIT_INTENT_RECEIVED = 2;
          }
          

          清单中的接收者声明:

          <receiver android:name=".autostart.AutostartDetector">
              <intent-filter>
                  <category android:name="com.example.autostart.CATEGORY_AUTOSTART"/>
                  <action android:name="com.example.autostart.ACTION_IMPLICIT_BROADCAST"/>
                  <action android:name="com.example.autostart.ACTION_EXPLICIT_BROADCAST"/>
              </intent-filter>
          </receiver>
          

          可靠地杀死应用程序是另一个问题。我一直在使用这个辅助方法:

          public static void killSelf(Context context) {
              ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
              activityManager.killBackgroundProcesses(context.getPackageName());
          
              if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
                  // this is all we can do before ICS. luckily Xiaomi phones have newer system versions :)
                  System.exit(1);
                  return;
              }
          
              // set up a callback so System.exit() is called as soon as all
              // the activities are finished
              context.registerComponentCallbacks(new ComponentCallbacks2() {
                  @Override
                  public void onTrimMemory(int i) {
                      if (i == TRIM_MEMORY_UI_HIDDEN) {
                          Log.v(TAG, "UI Hidden");
                          System.exit(1);
                      }
                  }
          
                  /* ... */
              });
          
              // see below
              ActivityTracker.getInstance().finishAllActivities();
          }
          

          ActivityTracker 是另一个跟踪活动生命周期的实用程序。确保在 Application 子类中注册它。

          @RequiresApi(api = Build.VERSION_CODES.ICE_CREAM_SANDWICH)
          public final class ActivityTracker implements Application.ActivityLifecycleCallbacks {
              private final ArraySet<Activity> mCreatedActivities = new ArraySet<>();
          
              public static ActivityTracker getInstance() {
                  return Holder.INSTANCE;
              }
          
              public static void init(Application application) {
                  application.registerActivityLifecycleCallbacks(getInstance());
              }
          
              public static void release(Application application) {
                  ActivityTracker activityTracker = getInstance();
                  application.unregisterActivityLifecycleCallbacks(activityTracker);
                  activityTracker.mCreatedActivities.clear();
              }
          
              public void finishAllActivities() {
                  // iterate over active activities and finish them all
                  for (Activity activity : mCreatedActivities) {
                      Log.v(TAG, "Finishing " + activity);
                      activity.finish();
                  }
              }
          
              public Set<Activity> getCreatedActivities() {
                  return Collections.unmodifiableSet(mCreatedActivities);
              }
          
              @Override
              public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
                  mCreatedActivities.add(activity);
              }    
          
              @Override
              public void onActivityDestroyed(Activity activity) {
                  mCreatedActivities.remove(activity);
              }
          
              private static final class Holder {
                  private static final ActivityTracker INSTANCE = new ActivityTracker();
              }
          
              /* ... */
          }
          

          您可能还想停止所有服务以确保安全。

          【讨论】:

          • 我没有收到任何广播你有任何其他方式或例子吗?
          • 你介意把它做成一个独立的图书馆和出版吗?我尝试使用它,但即使 Autostart 已关闭,我也能得到这两个意图。我也不清楚应该如何在这种方法中重新创建活动。
          【解决方案8】:

          100% 为OPPO、vivo、小米、乐视华为、荣耀工作

          调用这个函数

          private void addAutoStartup() {
          
              try {
                  Intent intent = new Intent();
                  String manufacturer = android.os.Build.MANUFACTURER;
                  if ("xiaomi".equalsIgnoreCase(manufacturer)) {
                      intent.setComponent(new ComponentName("com.miui.securitycenter", "com.miui.permcenter.autostart.AutoStartManagementActivity"));
                  } else if ("oppo".equalsIgnoreCase(manufacturer)) {
                      intent.setComponent(new ComponentName("com.coloros.safecenter", "com.coloros.safecenter.permission.startup.StartupAppListActivity"));
                  } else if ("vivo".equalsIgnoreCase(manufacturer)) {
                      intent.setComponent(new ComponentName("com.vivo.permissionmanager", "com.vivo.permissionmanager.activity.BgStartUpManagerActivity"));
                  } else if ("Letv".equalsIgnoreCase(manufacturer)) {
                      intent.setComponent(new ComponentName("com.letv.android.letvsafe", "com.letv.android.letvsafe.AutobootManageActivity"));
                  } else if ("Honor".equalsIgnoreCase(manufacturer)) {
                      intent.setComponent(new ComponentName("com.huawei.systemmanager", "com.huawei.systemmanager.optimize.process.ProtectActivity"));
                  }
          
                  List<ResolveInfo> list = getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
                  if  (list.size() > 0) {
                      startActivity(intent);
                  }
              } catch (Exception e) {
                  Log.e("exc" , String.valueOf(e));
              }
          }
          

          【讨论】:

          • 这不回答用户是否设置了权限。
          • 不幸的是,它不适用于 oppo A77。权限拒绝:启动 Intent { cmp=com.coloros.safecenter/.startupapp.StartupAppListActivity }。我也在清单中放了这个“oppo.permission.OPPO_COMPONENT_SAFE”。
          • 启动自动启动权限是对的,但是如何检查自动启动权限是否已启用。
          • @Shubham 你是怎么得到这些类名的?
          • 每次打开我的应用程序都会重定向,即使我已经授予了自动启动权限。如何处理? @Shubham
          【解决方案9】:

          除了Nikhil's answer

          首先,Facebook、Whatsapp 等一些应用程序默认被小米列入白名单,这意味着这些应用程序将自动开启自动启动权限。

          我也没有找到任何方法来检查自动启动权限是否已启用并以编程方式启用它。尽管如上答案表明我们可以将用户重定向到自动启动权限活动,但是当我们必须重定向用户时,我们仍然不知道,而且这也不适用于所有 Xiomi 设备。

          所以我使用了另一种方法来让我的同步适配器工作。我在共享首选项中存储了一个名为“isSyncAdapterRunning”的布尔变量,并在每次同步适配器运行时设置它的值。这样我就可以知道我的同步适配器是否正常工作。

          //in my sync adapter
          @Override
          public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult) {
              Log.e("TAG", "SyncStarted");
              performSync(true);        
          }
          
          public static void performSync(boolean fromSyncAdapterClass){
              //write your code for sync operation
              if(fromSyncAdapterClass){
                    setValueOfIsSyncAdapterRunningVariable();
              }
          }
          

          如果同步适配器不工作,我会创建其他后台服务来执行相同的任务。

          //In my other background service
          public class BackgroundSyncService extends IntentService {
          
          public BackgroundSyncService() {
              super("BackgroundSyncService");
          }
          
          @Override
          protected void onHandleIntent(Intent intent) {
              SyncAdapter.performSync(false);        
          }
          }
          

          现在启动同步适配器:

          // start your sync adapter here
          
          //And after that just start that service with a condition
          if(!getValueOfIsSyncAdapterRunningVariable()){
                startService(new Intent(context, BackgroundSyncService.class));
          }
          

          所以基本上,如果我的同步适配器不工作,我会在后台运行另一个服务来执行相同的任务,最好的事情是一次只有一个服务会运行。 如果用户打开自动启动权限并再次关闭,上述代码将失败,因为布尔变量的值已经设置。为此,您可以将布尔变量的值设置为每 24 小时一次的默认值。

          希望这会有所帮助。

          【讨论】:

            【解决方案10】:

            您必须允许和拒绝系统权限。

            下面是代码:

            private boolean checkPermission(){
                int result = ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION);
                if (result == PackageManager.PERMISSION_GRANTED){
            
                    return true;
            
                } else {
            
                    return false;
            
                }
            }
            
             @Override
             public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
                switch (requestCode) {
                    case PERMISSION_REQUEST_CODE:
                        if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            
                            Snackbar.make(view,"Permission Granted, Now you can access location data.",Snackbar.LENGTH_LONG).show();
            
                        } else {
            
                            Snackbar.make(view,"Permission Denied, You cannot access location data.",Snackbar.LENGTH_LONG).show();
            
                        }
                        break;
                }
            }
            

            【讨论】:

            • 自动启动权限是为了通知目的,我认为上述 LOCATION 的答案
            猜你喜欢
            • 2016-12-24
            • 2019-06-13
            • 2018-07-25
            • 2022-01-21
            • 2013-02-03
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多