diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index b0ce8ab3a..f3d05d832 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -12,12 +12,24 @@ tools:ignore="ProtectedPermissions" /> + + + + + + - \ No newline at end of file diff --git a/app/src/main/java/com/topjohnwu/magisk/AutoRootFragment.java b/app/src/main/java/com/topjohnwu/magisk/AutoRootFragment.java index 10f054b96..4207f34d6 100644 --- a/app/src/main/java/com/topjohnwu/magisk/AutoRootFragment.java +++ b/app/src/main/java/com/topjohnwu/magisk/AutoRootFragment.java @@ -9,7 +9,6 @@ import android.os.Bundle; import android.preference.PreferenceManager; import android.support.annotation.Nullable; import android.support.v4.app.ListFragment; -import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -30,7 +29,7 @@ public class AutoRootFragment extends ListFragment { private ApplicationAdapter listadaptor = null; public ListView listView; public SharedPreferences prefs; - List arrayBlackList, arrayWhiteList; + List arrayBlackList; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { @@ -49,9 +48,6 @@ public class AutoRootFragment extends ListFragment { set.add("com.google.android.apps.walletnfcrel"); set.add("com.google.android.gms"); editor.putStringSet("auto_blacklist", set); - set.clear(); - set.add("com.kermidas.TitaniumBackupPro"); - editor.putStringSet("auto_whitelist", set); editor.apply(); } new LoadApplications().execute(); @@ -75,29 +71,15 @@ public class AutoRootFragment extends ListFragment { private void ToggleApp(String appToCheck, int position, View v) { Set blackListSet = prefs.getStringSet("auto_blacklist", null); - Set whiteListSet = prefs.getStringSet("auto_whitelist", null); + assert blackListSet != null; arrayBlackList = new ArrayList<>(blackListSet); - assert whiteListSet != null; - arrayWhiteList = new ArrayList<>(whiteListSet); - Log.d("Magisk", "Trying to toggle for " + appToCheck + " stringset is " + arrayBlackList.toString()); - if ((!arrayBlackList.contains(appToCheck)) && (!arrayWhiteList.contains(appToCheck))) { - Log.d("Magisk", "App is not in any array, adding to whitelist"); - arrayWhiteList.add(appToCheck); - - } else if (arrayWhiteList.contains(appToCheck)) { - Log.d("Magisk", "App is in whitelist, moving to blacklist"); - for (int i = 0; i < arrayWhiteList.size(); i++) { - if (appToCheck.equals(arrayWhiteList.get(i))) { - arrayWhiteList.remove(i); - } - } + if (!arrayBlackList.contains(appToCheck)) { arrayBlackList.add(appToCheck); - } else if (arrayBlackList.contains(appToCheck)) { - Log.d("Magisk", "App is in Blacklist, removing"); + } else { for (int i = 0; i < arrayBlackList.size(); i++) { if (appToCheck.equals(arrayBlackList.get(i))) { arrayBlackList.remove(i); @@ -105,12 +87,7 @@ public class AutoRootFragment extends ListFragment { } } - Set set2 = new HashSet<>(arrayBlackList); - Log.d("Magisk", "Committing set, value is: " + set2); - SharedPreferences.Editor editor = prefs.edit(); - editor.putStringSet("auto_blacklist", new HashSet<>(arrayBlackList)); - editor.putStringSet("auto_whitelist", new HashSet<>(arrayWhiteList)); - editor.apply(); + prefs.edit().putStringSet("auto_blacklist", new HashSet<>(arrayBlackList)).apply(); listadaptor.UpdateRootStatusView(position, v); } diff --git a/app/src/main/java/com/topjohnwu/magisk/MonitorService.java b/app/src/main/java/com/topjohnwu/magisk/MonitorService.java index 3ac25a528..49faf019e 100644 --- a/app/src/main/java/com/topjohnwu/magisk/MonitorService.java +++ b/app/src/main/java/com/topjohnwu/magisk/MonitorService.java @@ -1,36 +1,28 @@ package com.topjohnwu.magisk; +import android.accessibilityservice.AccessibilityService; +import android.accessibilityservice.AccessibilityServiceInfo; import android.app.NotificationManager; import android.app.PendingIntent; -import android.app.Service; -import android.app.usage.UsageStats; -import android.app.usage.UsageStatsManager; -import android.content.Context; +import android.content.ComponentName; import android.content.Intent; import android.content.SharedPreferences; +import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.os.Build; import android.os.Handler; -import android.os.IBinder; import android.preference.PreferenceManager; -import android.support.annotation.Nullable; import android.support.v4.app.NotificationCompat; import android.util.Log; +import android.view.accessibility.AccessibilityEvent; -import com.topjohnwu.magisk.utils.Shell; import com.topjohnwu.magisk.utils.Utils; import java.util.ArrayList; -import java.util.List; import java.util.Set; -import java.util.SortedMap; -import java.util.TreeMap; - -public class MonitorService extends Service - -{ +public class MonitorService extends AccessibilityService { private static final String TAG = "Magisk"; private final Handler handler = new Handler(); private Boolean disableroot; @@ -38,65 +30,85 @@ public class MonitorService extends Service private int counter = 0; private String mPackageName = ""; - @Nullable @Override - public IBinder onBind(Intent intent) { - return null; - } + protected void onServiceConnected() { + super.onServiceConnected(); - @Override - public int onStartCommand(Intent intent, int flags, int startId) { - return START_STICKY; + //Configure these here for compatibility with API 13 and below. + AccessibilityServiceInfo config = new AccessibilityServiceInfo(); + config.eventTypes = AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED; + config.feedbackType = AccessibilityServiceInfo.FEEDBACK_GENERIC; + disableroot = false; + disablerootprev = disableroot; + if (Build.VERSION.SDK_INT >= 16) + //Just in case this helps + config.flags = AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS; + + setServiceInfo(config); } @Override public void onCreate() { super.onCreate(); - checkProcesses.run(); Log.d("Magisk","MonitorService: Service created"); } @Override - public void onDestroy() { - super.onDestroy(); - Log.d("Magisk","MonitorService: Service destroyed"); + public void onAccessibilityEvent(AccessibilityEvent event) { + if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) { + ComponentName componentName = new ComponentName( + event.getPackageName().toString(), + event.getClassName().toString() + ); + + ActivityInfo activityInfo = tryGetActivity(componentName); + boolean isActivity = activityInfo != null; + if (isActivity) { + Log.i("Magisk","CurrentActivity: " + componentName.getPackageName()); + String mPackage = componentName.getPackageName(); + + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); + if (prefs.getBoolean("autoRootEnable", false)) { + + Set setBlackList = prefs.getStringSet("auto_blacklist", null); + Set setWhiteList = prefs.getStringSet("auto_whitelist", null); + + if (setBlackList != null) { + disableroot = setBlackList.contains(mPackage); + if (disableroot) { + ForceDisableRoot(); + } else { + ForceEnableRoot(); + } + String appFriendly = getAppName(mPackage); + ShowNotification(disableroot,appFriendly); + } + } + } + } } - private Runnable checkProcesses = new Runnable() { - @Override - public void run() { - - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); - if (prefs.getBoolean("autoRootEnable", false)) { - - Set setBlackList = prefs.getStringSet("auto_blacklist", null); - Set setWhiteList = prefs.getStringSet("auto_whitelist", null); - - if (setBlackList != null) { - disableroot = getStats(setBlackList); - } - - if (disableroot != disablerootprev) { - - String rootstatus = (disableroot ? "disabled" : "enabled"); - if (disableroot) { - ForceDisableRoot(); - } else { - ForceEnableRoot(); - } - - ShowNotification(disableroot); - - } - disablerootprev = disableroot; - //Log.d(TAG,"Root check completed, set to " + (disableroot ? "disabled" : "enabled")); - - } - handler.postDelayed(checkProcesses, 500); + private String getAppName (String packageName) { + PackageManager pkManager = getPackageManager(); + ApplicationInfo appInfo; + String appname = ""; + try { + appInfo = pkManager.getApplicationInfo(packageName, 0); + appname = (String) ((appInfo != null) ? pkManager.getApplicationLabel(appInfo) : "???"); + return appname; + } catch (final PackageManager.NameNotFoundException e) { + return ""; } + } - }; + private ActivityInfo tryGetActivity(ComponentName componentName) { + try { + return getPackageManager().getActivityInfo(componentName, 0); + } catch (PackageManager.NameNotFoundException e) { + return null; + } + } private void ForceDisableRoot() { Log.d("Magisk", "MonitorService: Forcedisable called."); @@ -113,10 +125,10 @@ public class MonitorService extends Service Utils.toggleRoot(true); if (!Utils.rootEnabled()) { Utils.toggleRoot(true); - } + } } - private void ShowNotification(boolean rootAction) { + private void ShowNotification(boolean rootAction, String packageName) { NotificationManager mNotifyMgr = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); NotificationCompat.Builder mBuilder; @@ -130,10 +142,10 @@ public class MonitorService extends Service 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); - if (mPackageName.equals("")) { + if (packageName.equals("")) { rootMessage = "Root has been disabled"; } else { - rootMessage = "Root has been disabled for " + mPackageName; + rootMessage = "Root has been disabled for " + packageName; } mBuilder = new NotificationCompat.Builder(getApplicationContext()) @@ -149,64 +161,7 @@ public class MonitorService extends Service } - private boolean getStats(Set seti) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - boolean inStats = false; - if (seti != null) { - ArrayList statList = new ArrayList<>(seti); - for (int i = 0; i < statList.size(); i++) { - if (isAppForeground(statList.get(i))) { - inStats = (isAppForeground(statList.get(i))); - } - } - return inStats; - } - Log.d(TAG, "SDK check failed."); - } - return false; + @Override + public void onInterrupt() { } - - private String getAppName (String packageName) { - PackageManager pkManager = getPackageManager(); - ApplicationInfo appInfo; - String appname = ""; - try { - appInfo = pkManager.getApplicationInfo(packageName, 0); - appname = (String) ((appInfo != null) ? pkManager.getApplicationLabel(appInfo) : "???"); - return appname; - } catch (final PackageManager.NameNotFoundException e) { - return null; - } - } - - - protected boolean isAppForeground(String packageName) { - UsageStatsManager usageStatsManager = (UsageStatsManager) getSystemService(Context.USAGE_STATS_SERVICE); - long time = System.currentTimeMillis(); - List stats = usageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_DAILY, time - 1000 * 10, time); - String topPackageName = ""; - if (stats != null) { - - SortedMap mySortedMap = new TreeMap<>(); - for (UsageStats usageStats : stats) { - mySortedMap.put(usageStats.getLastTimeUsed(), usageStats); - - } - if (!mySortedMap.isEmpty()) { - topPackageName = mySortedMap.get(mySortedMap.lastKey()).getPackageName(); - if (topPackageName.equals("com.topjohnwu.magisk")) { - mySortedMap.remove(mySortedMap.lastKey()); - topPackageName = mySortedMap.get(mySortedMap.lastKey()).getPackageName(); - Log.d("Magisk","MonitorService: Package is " + topPackageName); - mPackageName = getAppName(topPackageName); - } - - } - } - return topPackageName.equals(packageName); - - - } - -} - +} \ No newline at end of file diff --git a/app/src/main/java/com/topjohnwu/magisk/RootFragment.java b/app/src/main/java/com/topjohnwu/magisk/RootFragment.java index fe87ace29..7865c76a9 100644 --- a/app/src/main/java/com/topjohnwu/magisk/RootFragment.java +++ b/app/src/main/java/com/topjohnwu/magisk/RootFragment.java @@ -9,7 +9,6 @@ import android.os.AsyncTask; import android.os.Bundle; import android.os.Handler; import android.preference.PreferenceManager; -import android.provider.Settings; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.util.Log; @@ -126,13 +125,7 @@ public class RootFragment extends Fragment { } - private void CheckAccessPermissions() { - if (!Utils.hasStatsPermission(getActivity())) { - Toast.makeText(getActivity(),"Please allow Usage Access for auto root to work.",Toast.LENGTH_LONG).show(); - startActivityForResult(new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS), 100); - } - } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { @@ -141,24 +134,27 @@ public class RootFragment extends Fragment { // Make sure the request was successful if (resultCode == Activity.RESULT_OK) { Log.d("Magisk","Got result code OK for permissions"); - Toast.makeText(getActivity(),"Auto-root disabled, permissions required.",Toast.LENGTH_LONG).show(); } else { autoRootToggle.setEnabled(false); + Toast.makeText(getActivity(),"Auto-root disabled, permissions required.",Toast.LENGTH_LONG).show(); + } } } private void ToggleAutoRoot(boolean toggleState) { - CheckAccessPermissions(); - if (Utils.hasStatsPermission(getActivity())) { autoRootStatus = toggleState; SharedPreferences.Editor editor = prefs.edit(); editor.putBoolean("autoRootEnable", (toggleState)); editor.apply(); if (toggleState) { + if (!Utils.hasStatsPermission(getActivity(),"com.topjohnwu.magisk/WindowChangeDetectingService")) { + Intent intent = new Intent(android.provider.Settings.ACTION_ACCESSIBILITY_SETTINGS); + startActivityForResult(intent, 100); + } Intent myIntent = new Intent(getActivity(), MonitorService.class); getActivity().startService(myIntent); rootToggle.setEnabled(false); @@ -172,7 +168,7 @@ public class RootFragment extends Fragment { getActivity().stopService(myIntent); rootToggle.setEnabled(true); } - } + } @Override diff --git a/app/src/main/java/com/topjohnwu/magisk/WelcomeActivity.java b/app/src/main/java/com/topjohnwu/magisk/WelcomeActivity.java index 8b83f88cd..309847076 100644 --- a/app/src/main/java/com/topjohnwu/magisk/WelcomeActivity.java +++ b/app/src/main/java/com/topjohnwu/magisk/WelcomeActivity.java @@ -9,7 +9,6 @@ import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.preference.PreferenceManager; -import android.provider.Settings; import android.support.annotation.IdRes; import android.support.annotation.NonNull; import android.support.design.widget.NavigationView; @@ -86,7 +85,7 @@ public class WelcomeActivity extends AppCompatActivity implements NavigationView @Override public void onDrawerOpened(View drawerView) { super.onDrawerOpened(drawerView); - super.onDrawerSlide(drawerView, 0); // this disables the arrow @ completed state + super.onDrawerSlide(drawerView, 0); // this disables the arrow @ completed tate } @Override diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/ApplicationAdapter.java b/app/src/main/java/com/topjohnwu/magisk/utils/ApplicationAdapter.java index b83e8c8ef..9a108f591 100644 --- a/app/src/main/java/com/topjohnwu/magisk/utils/ApplicationAdapter.java +++ b/app/src/main/java/com/topjohnwu/magisk/utils/ApplicationAdapter.java @@ -9,6 +9,7 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; +import android.widget.CheckBox; import android.widget.ImageView; import android.widget.TextView; @@ -24,8 +25,6 @@ public class ApplicationAdapter extends ArrayAdapter { private PackageManager packageManager; public ArrayList arrayList; public SharedPreferences prefs; - private int BLACKLIST_LIST = 1; - private int WHITELIST_LIST = 2; public ApplicationAdapter(Context context, int textViewResourceId, List appsList) { @@ -66,14 +65,14 @@ public class ApplicationAdapter extends ArrayAdapter { TextView appName = (TextView) view.findViewById(R.id.app_name); TextView packageName = (TextView) view.findViewById(R.id.app_paackage); ImageView iconview = (ImageView) view.findViewById(R.id.app_icon); - ImageView statusview = (ImageView) view.findViewById(R.id.app_status); + CheckBox statusview = (CheckBox) view.findViewById(R.id.checkbox); appName.setText(applicationInfo.loadLabel(packageManager)); packageName.setText(applicationInfo.packageName); iconview.setImageDrawable(applicationInfo.loadIcon(packageManager)); - if (CheckApp(applicationInfo.packageName, BLACKLIST_LIST)) { - statusview.setImageDrawable(this.context.getDrawable(R.drawable.root)); + if (CheckApp(applicationInfo.packageName)) { + statusview.setChecked(true); } else { - statusview.setImageDrawable(null); + statusview.setChecked(false); } } return view; @@ -88,21 +87,18 @@ public class ApplicationAdapter extends ArrayAdapter { } ApplicationInfo applicationInfo = appsList.get(position); if (null != applicationInfo) { - ImageView statusview = (ImageView) view.findViewById(R.id.app_status); - if (CheckApp(applicationInfo.packageName, BLACKLIST_LIST)) { - statusview.setImageDrawable(this.context.getDrawable(R.drawable.root)); - } else if (CheckApp(applicationInfo.packageName, WHITELIST_LIST)) { - statusview.setImageDrawable(this.context.getDrawable(R.drawable.ic_stat_notification_autoroot_off)); + CheckBox statusview = (CheckBox) view.findViewById(R.id.checkbox); + if (CheckApp(applicationInfo.packageName)) { + statusview.setChecked(true); } else { - statusview.setImageDrawable(null); + statusview.setChecked(false); } } } - private boolean CheckApp(String appToCheck, int list) { + private boolean CheckApp(String appToCheck) { boolean starter = false; - if (list == BLACKLIST_LIST) { Set set = prefs.getStringSet("auto_blacklist", null); if (set != null) { arrayList = new ArrayList<>(set); @@ -113,18 +109,6 @@ public class ApplicationAdapter extends ArrayAdapter { } } - } else { - Set set = prefs.getStringSet("auto_whitelist", null); - if (set != null) { - arrayList = new ArrayList<>(set); - for (String string : set) { - if (string.equals(appToCheck)) { - starter = true; - } - } - } - - } return starter; } diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/Utils.java b/app/src/main/java/com/topjohnwu/magisk/utils/Utils.java index 8987096b9..5ef1ed198 100644 --- a/app/src/main/java/com/topjohnwu/magisk/utils/Utils.java +++ b/app/src/main/java/com/topjohnwu/magisk/utils/Utils.java @@ -1,6 +1,7 @@ package com.topjohnwu.magisk.utils; import android.Manifest; +import android.accessibilityservice.AccessibilityServiceInfo; import android.app.Activity; import android.app.ActivityManager; import android.app.AppOpsManager; @@ -23,6 +24,8 @@ import android.support.v7.app.AlertDialog; import android.util.Base64; import android.util.Log; import android.view.View; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityManager; import android.widget.Toast; import com.topjohnwu.magisk.ModulesFragment; @@ -171,14 +174,24 @@ public class Utils { return value; } - public static boolean hasStatsPermission(Context context) { - AppOpsManager appOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); - int mode = appOps.checkOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS, - android.os.Process.myUid(), context.getPackageName()); - return mode == AppOpsManager.MODE_ALLOWED; + + public static boolean hasStatsPermission(Context context, String id) { + + AccessibilityManager am = (AccessibilityManager) context + .getSystemService(Context.ACCESSIBILITY_SERVICE); + + List runningServices = am + .getEnabledAccessibilityServiceList(AccessibilityEvent.TYPES_ALL_MASK); + for (AccessibilityServiceInfo service : runningServices) { + if (id.equals(service.getId())) { + return true; + } } + return false; + } + public abstract static class DownloadReceiver extends BroadcastReceiver { public Context mContext; diff --git a/app/src/main/res/layout/app_list_row.xml b/app/src/main/res/layout/app_list_row.xml index 0b7a2d156..793f481a3 100644 --- a/app/src/main/res/layout/app_list_row.xml +++ b/app/src/main/res/layout/app_list_row.xml @@ -1,59 +1,73 @@ - - + + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_gravity="center_vertical" + android:padding="10dp"> - + + + - - - - - - - - - - - + android:layout_toEndOf="@+id/app_icon" + android:gravity="center_vertical" + android:paddingEnd="3dp" + android:paddingStart="3dp" + android:textStyle="bold" /> + + + + + + - \ No newline at end of file + + + + diff --git a/app/src/main/res/layout/auto_root_fragment.xml b/app/src/main/res/layout/auto_root_fragment.xml index 214589074..3984d2a31 100644 --- a/app/src/main/res/layout/auto_root_fragment.xml +++ b/app/src/main/res/layout/auto_root_fragment.xml @@ -6,7 +6,10 @@ + android:divider="@android:color/transparent" + android:dividerHeight="10.0sp" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_margin="15dp" /> \ No newline at end of file diff --git a/app/src/main/res/layout/list_item_repo.xml b/app/src/main/res/layout/list_item_repo.xml index b8bcb8f61..ef78a023e 100644 --- a/app/src/main/res/layout/list_item_repo.xml +++ b/app/src/main/res/layout/list_item_repo.xml @@ -5,10 +5,7 @@ android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_gravity="center" - android:layout_marginBottom="3dip" - android:layout_marginLeft="8dip" - android:layout_marginRight="8dip" - android:layout_marginTop="3dip" + android:layout_margin="8dp" android:background="?android:attr/selectableItemBackground" android:minHeight="?android:attr/listPreferredItemHeight" card_view:cardCornerRadius="2dp" diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 74b9fea33..c4801921f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -82,6 +82,8 @@ This feature will not work without permission to write external storage. No root access, functionality limited No thanks + Accessibility service required for Magisk auto-unroot feature + Magisk Manager %1$s Update! New version v%2$s of %1$s is available!\nChangelog:\n%3$s Download and install diff --git a/app/src/main/res/xml/accessibilityservice.xml b/app/src/main/res/xml/accessibilityservice.xml new file mode 100644 index 000000000..08bf96e91 --- /dev/null +++ b/app/src/main/res/xml/accessibilityservice.xml @@ -0,0 +1,11 @@ + + + \ No newline at end of file