Index: chrome/android/java/src/org/chromium/chrome/browser/init/AndroidIPCWatcher.java |
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/init/AndroidIPCWatcher.java b/chrome/android/java/src/org/chromium/chrome/browser/init/AndroidIPCWatcher.java |
new file mode 100644 |
index 0000000000000000000000000000000000000000..f4fde74ed641777e08628cfb68df282985915ab2 |
--- /dev/null |
+++ b/chrome/android/java/src/org/chromium/chrome/browser/init/AndroidIPCWatcher.java |
@@ -0,0 +1,199 @@ |
+// Copyright 2016 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+package org.chromium.chrome.browser.init; |
+ |
+import android.annotation.TargetApi; |
+import android.content.ContentResolver; |
+import android.content.Context; |
+import android.content.pm.PackageManager; |
+import android.hardware.input.InputManager; |
+import android.location.LocationManager; |
+import android.net.ConnectivityManager; |
+import android.os.Build; |
+import android.os.Looper; |
+import android.os.SystemClock; |
+import android.os.UserManager; |
+import android.view.accessibility.AccessibilityManager; |
+ |
+import org.chromium.base.Log; |
+ |
+import java.lang.reflect.Field; |
+import java.lang.reflect.InvocationHandler; |
+import java.lang.reflect.Method; |
+import java.lang.reflect.Proxy; |
+import java.util.Arrays; |
+ |
+public class AndroidIPCWatcher { |
+ private static final Looper sMainLooper = Looper.getMainLooper(); |
+ private static boolean sProxiesInstalled; |
+ private static final String TAG = "AndroidIPCWatcher"; |
+ |
+ private static final class IPCListener implements InvocationHandler { |
+ private final Object mSystemImpl; |
+ |
+ public IPCListener(Object systemImpl) { |
+ mSystemImpl = systemImpl; |
+ } |
+ |
+ @Override |
+ @TargetApi(Build.VERSION_CODES.KITKAT) |
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { |
+ long initial = SystemClock.elapsedRealtimeNanos(); |
+ Object returnValue = method.invoke(mSystemImpl, args); |
+ long after = SystemClock.elapsedRealtimeNanos(); |
+ |
+ // ignore non-main thread |
+ if (!sMainLooper.equals(Looper.myLooper())) return returnValue; |
+ |
+ if (after - initial > 10e6) { |
+ Log.e(TAG, mSystemImpl.getClass().getName() + "#" + method.getName() + " took " |
+ + (after - initial) / 1000 + " us"); |
+ Log.e(TAG, "stack=" + Arrays.toString(Thread.currentThread().getStackTrace())); |
+ } |
+ return returnValue; |
+ } |
+ } |
+ |
+ @SuppressWarnings({"rawtypes", "unchecked" }) |
+ public static void install(Context context) { |
+ if (sProxiesInstalled) return; |
+ sProxiesInstalled = true; |
+ |
+ // 1. Package Manager |
+ try { |
+ PackageManager packageManager = context.getPackageManager(); |
+ Field field = packageManager.getClass().getDeclaredField("mPM"); |
+ field.setAccessible(true); |
+ Object listener = Proxy.newProxyInstance(context.getClassLoader(), new Class[] { |
+ Class.forName("android.content.pm.IPackageManager") }, |
+ new IPCListener(field.get(packageManager))); |
+ field.set(packageManager, listener); |
+ } catch (Throwable throwable) { |
+ Log.d(TAG, "Failed to intercept IPackageManager: " + throwable); |
+ } |
+ |
+ // 2. ContentResolver |
+ |
+ // Force internal ContentService to get assigned |
+ ContentResolver.getSyncAdapterTypes(); |
+ try { |
+ Field field = ContentResolver.class.getDeclaredField("sContentService"); |
+ field.setAccessible(true); |
+ Object listener = Proxy.newProxyInstance(context.getClassLoader(), new Class[] { |
+ Class.forName("android.content.IContentService") }, |
+ new IPCListener(field.get(ContentResolver.class))); |
+ field.set(ContentResolver.class, listener); |
+ } catch (Throwable throwable) { |
+ Log.d(TAG, "Failed to intercept IContentService: " + throwable); |
+ } |
+ |
+ // 3. UserManager |
+ try { |
+ UserManager manager = (UserManager) context.getSystemService(Context.USER_SERVICE); |
+ Field field = manager.getClass().getDeclaredField("mService"); |
+ field.setAccessible(true); |
+ Object listener = Proxy.newProxyInstance(context.getClassLoader(), new Class[] { |
+ Class.forName("android.os.IUserManager") }, |
+ new IPCListener(field.get(manager))); |
+ field.set(manager, listener); |
+ } catch (Throwable throwable) { |
+ Log.d(TAG, "Failed to intercept IUserManager: " + throwable); |
+ } |
+ |
+ // 4. InputManager |
+ try { |
+ InputManager manager = (InputManager) context.getSystemService(Context.INPUT_SERVICE); |
+ Field field = manager.getClass().getDeclaredField("mIm"); |
+ field.setAccessible(true); |
+ Object listener = Proxy.newProxyInstance(context.getClassLoader(), new Class[] { |
+ Class.forName("android.hardware.input.IInputManager") }, |
+ new IPCListener(field.get(manager))); |
+ field.set(manager, listener); |
+ } catch (Throwable throwable) { |
+ Log.d(TAG, "Failed to intercept InputManager: " + throwable); |
+ } |
+ |
+ // 5. LocationManager |
+ try { |
+ LocationManager manager = |
+ (LocationManager) context.getSystemService(Context.LOCATION_SERVICE); |
+ Field field = manager.getClass().getDeclaredField("mService"); |
+ field.setAccessible(true); |
+ Object listener = Proxy.newProxyInstance(context.getClassLoader(), new Class[] { |
+ Class.forName("android.location.ILocationManager") }, |
+ new IPCListener(field.get(manager))); |
+ field.set(manager, listener); |
+ } catch (Throwable throwable) { |
+ Log.d(TAG, "Failed to intercept LocationManager: " + throwable); |
+ } |
+ |
+ |
+ // 6. ActivityManager |
+ try { |
+ Class activityManagerNativeClass = Class.forName("android.app.ActivityManagerNative"); |
+ Method defaultMethod = activityManagerNativeClass.getMethod("getDefault", |
+ new Class[] {}); |
+ Object iActivityManager = defaultMethod.invoke(activityManagerNativeClass); |
+ Object listener = Proxy.newProxyInstance(context.getClassLoader(), new Class[] { |
+ Class.forName("android.app.IActivityManager") }, |
+ new IPCListener(iActivityManager)); |
+ |
+ Field field = activityManagerNativeClass.getDeclaredField("gDefault"); |
+ field.setAccessible(true); |
+ Object singleton = field.get(activityManagerNativeClass); |
+ |
+ Class singletonClass = Class.forName("android.util.Singleton"); |
+ field = singletonClass.getDeclaredField("mInstance"); |
+ field.setAccessible(true); |
+ field.set(singleton, listener); |
+ } catch (Throwable throwable) { |
+ Log.d(TAG, "Failed to intercept ActivityManager: " + throwable); |
+ } |
+ |
+ // 7. LockSettings |
+ try { |
+ android.provider.Settings.Secure.getString(context.getContentResolver(), "foo"); |
+ Class manager = android.provider.Settings.Secure.class; |
+ Field field = manager.getDeclaredField("sLockSettings"); |
+ field.setAccessible(true); |
+ Object listener = Proxy.newProxyInstance(context.getClassLoader(), new Class[] { |
+ Class.forName("com.android.internal.widget.ILockSettings") }, |
+ new IPCListener(field.get(manager))); |
+ field.set(manager, listener); |
+ } catch (Throwable throwable) { |
+ throwable.printStackTrace(); |
+ Log.d(TAG, "Failed to intercept LockManager: " + throwable); |
+ } |
+ |
+ // 8. AccessibilityManager |
+ try { |
+ AccessibilityManager manager = |
+ (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE); |
+ Field field = manager.getClass().getDeclaredField("mService"); |
+ field.setAccessible(true); |
+ Object listener = Proxy.newProxyInstance(context.getClassLoader(), new Class[] { |
+ Class.forName("android.view.accessibility.IAccessibilityManager") }, |
+ new IPCListener(field.get(manager))); |
+ field.set(manager, listener); |
+ } catch (Throwable throwable) { |
+ Log.d(TAG, "Failed to intercept AccessibilityManager: " + throwable); |
+ } |
+ // 9. ConnectivityManager |
+ try { |
+ ConnectivityManager manager = |
+ (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); |
+ Field field = manager.getClass().getDeclaredField("mService"); |
+ field.setAccessible(true); |
+ Object listener = Proxy.newProxyInstance(context.getClassLoader(), new Class[] { |
+ Class.forName("android.net.IConnectivityManager") }, |
+ new IPCListener(field.get(manager))); |
+ field.set(manager, listener); |
+ } catch (Throwable throwable) { |
+ Log.d(TAG, "Failed to intercept ConnectivityManager: " + throwable); |
+ } |
+ |
+ |
+ } |
+} |