android developer
android developer

Reputation: 115990

Is it possible using adb via root to grant the permission of PACKAGE_USAGE_STATS?

Background

adb can be used to grant various permissions, but this permission is special, and using it for this permission via root doesn't seem to work properly:

pm grant packageName android.permission.PACKAGE_USAGE_STATS

The problem

The OS says it's granted, but the app itself can't detect it.

What I've found

See code of the app to check it out:

class MainActivity : AppCompatActivity() {
    companion object {
        @TargetApi(Build.VERSION_CODES.LOLLIPOP)
        @JvmStatic
        fun hasPermission(context: Context): Boolean {
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
                return false
            val appOps = context.getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager
            val mode = appOps.checkOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS, android.os.Process.myUid(), context.getPackageName())
            val granted = if (mode == AppOpsManager.MODE_DEFAULT) {
                (context.checkCallingOrSelfPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) == PackageManager.PERMISSION_GRANTED)
            } else {
                (mode == AppOpsManager.MODE_ALLOWED)
            }
            return granted
//            val appOps = context.getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager
//            val granted = appOps.checkOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS, android.os.Process.myUid(), context.packageName) == AppOpsManager.MODE_ALLOWED
//            return granted
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        button.setOnClickListener {
            val granted = hasPermission(this@MainActivity)
            Toast.makeText(this@MainActivity, "has permission:" + granted, Toast.LENGTH_SHORT).show()
        }
        button2.setOnClickListener {
            startActivity(Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS))
        }
    // extra code for granting the app the permission using root
    }
}

manifest:

<manifest package="com.example.user.myapplication" xmlns:android="http://schemas.android.com/apk/res/android"
          xmlns:tools="http://schemas.android.com/tools">

    <uses-permission
        android:name="android.permission.PACKAGE_USAGE_STATS" tools:ignore="ProtectedPermissions"/>
    <application
        android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
    </application>

</manifest>

layout file

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
    android:layout_height="match_parent" android:gravity="center" android:orientation="vertical"
    tools:context=".MainActivity">

    <TextView
        android:layout_width="wrap_content" android:layout_height="wrap_content"
        android:text="check the permission using below buttons before and after you grant it via root"/>

    <Button
        android:id="@+id/button" android:layout_width="wrap_content" android:layout_height="wrap_content"
        android:text="check permission via code"/>

    <Button
        android:id="@+id/button2" android:layout_width="wrap_content" android:layout_height="wrap_content"
        android:text="go to settings screen"/>

</LinearLayout>

The question

How come if I use adb via PC, it works fine, yet if I use root within the app itself, I get a weird result, that the OS says the app got the permission, yet the app can't detect it ?

What should I do in order to truly grant the permission using root, within the app, and also detect it properly ?

Upvotes: 3

Views: 4597

Answers (2)

Xavier Maurer
Xavier Maurer

Reputation: 1

In case you face the same problem I found this answer Grant PACKAGE_USAGE_STATS permission through adb and combined Jared Rummler's answer it finally worked for me on a rooted device.

public void setMissingPermissions(String pkg){
        try {
            String[] Commands = new String[]{"su", "-c", "pm", "grant", pkg, "android.permission.PACKAGE_USAGE_STATS"};
            Runtime.getRuntime().exec(Commands);
            Commands = new String[]{"su", "-c", "appops", "set", pkg, "GET_USAGE_STATS", "allow"};
            Runtime.getRuntime().exec(Commands);
        }
        catch(Exception e){
        }
}

Upvotes: 0

Jared Rummler
Jared Rummler

Reputation: 38131

Using adb shell pm grant [PACKAGE_NAME] android.permission.PACKAGE_USAGE_STATS worked for me on several devices.

Make sure you added the following to the AndroidManifest:

<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"
  tools:ignore="ProtectedPermissions"/>

I was also able to use root to grant the permission. Again, the app must declare the permission in the AndroidManifest.

You can use AndroidShell to run the command as root. Example:

String packageName = context.getPackageName();
CommandResult result = Shell.SU.run("pm grant " + packageName + " android.permission.PACKAGE_USAGE_STATS");

As mentioned by Sagar, you can also ask the user to grant the permission in the settings app, which is Google's recommended way to do this:

startActivity(new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS));

Upvotes: 1

Related Questions