1. 程式人生 > >利用JAVA反射機制訪問ITelephony隱藏介面(android程式中結束通話電話)

利用JAVA反射機制訪問ITelephony隱藏介面(android程式中結束通話電話)

這兩天研究如何利用程式結束通話電話。發現在Android1.0的時候Phone中提供了提供了endCall方法,而1.5以後這個方法被設定為私有了。

如此有了如下研究:

 /**
  * 利用JAVA反射機制呼叫ITelephony的endCall()結束通話。
  */
 private void endCall() {
  // 初始化iTelephony
  Class<TelephonyManager> c = TelephonyManager.class;
  Method getITelephonyMethod = null;
  try {
   // 獲取所有public/private/protected/預設
   // 方法的函式,如果只需要獲取public方法,則可以呼叫getMethod.
   getITelephonyMethod = c.getDeclaredMethod("getITelephony",
     (Class[]) null);
   // 將要執行的方法物件設定是否進行訪問檢查,也就是說對於public/private/protected/預設
   // 我們是否能夠訪問。值為 true 則指示反射的物件在使用時應該取消 Java 語言訪問檢查。值為 false
   // 則指示反射的物件應該實施 Java 語言訪問檢查。
   getITelephonyMethod.setAccessible(true);
  } catch (SecurityException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  } catch (NoSuchMethodException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }

  try {
   ITelephony iTelephony = (ITelephony) getITelephonyMethod.invoke(
     tManager, (Object[]) null);
   try {
    iTelephony.endCall();
   } catch (RemoteException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }
  } catch (IllegalArgumentException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  } catch (IllegalAccessException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  } catch (InvocationTargetException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
 }

/******************************************************************************************/
     以下摘自---百度空間---描述瞭如何利用反射進行私有方法的單元測試
/******************************************************************************************/
利用安全管理器
安全性管理器與反射機制相結合,也可以達到我們的目的。Java 執行時依靠一種安全性管理器來檢驗呼叫程式碼對某一特定的訪問而言是否有足夠的許可權。具體來說,安全性管理器是 java.lang.SecurityManager 類或擴充套件自該類的一個類,且它在執行時檢查某些應用程式操作的許可權。換句話說,所有的物件訪問在執行自身邏輯之前都必須委派給安全管理器,當訪問受到安全性管理器的控制,應用程式就只能執行那些由相關安全策略特別准許的操作。因此安全管理器一旦啟動可以為程式碼提供足夠的保護。預設情況下,安全性管理器是沒有被設定的,除非程式碼明確地安裝一個預設的或定製的安全管理器,否則執行時的訪問控制檢查並不起作用。我們可以通過這一點在執行時避開 Java 的訪問控制檢查,達到我們訪問非公有成員變數或方法的目的。為能訪問我們需要的非公有成員,我們還需要使用 Java 反射技術。Java 反射是一種強大的工具,它使我們可以在執行時裝配程式碼,而無需在物件之間進行原始碼連結,從而使程式碼更具靈活性。在編譯時,Java 編譯程式保證了私有成員的私有特性,從而一個類的私有方法和私有成員變數不能被其他類靜態引用。然而,通過 Java 反射機制使得我們可以在執行時查詢以及訪問變數和方法。由於反射是動態的,因此編譯時的檢查就不再起作用了。


下面的程式碼演示瞭如何利用安全性管理器與反射機制訪問私有變數。
清單 3. 利用反射機制訪問類的成員變數

 // 獲得指定變數的值 
 public static Object getValue(Object instance, String fieldName) 
       throws    IllegalAccessException, NoSuchFieldException ... {
 
      Field field = getField(instance.getClass(), fieldName);
      // 引數值為true,禁用訪問控制檢查 
      field.setAccessible( true );
      return field.get(instance);
 } 
 
 // 該方法實現根據變數名獲得該變數的值 
 public static Field getField(Class thisClass, String fieldName) 
       throws NoSuchFieldException ... {
 
       if (thisClass == null ) ... {
          throw new NoSuchFieldException( " Error field ! " );
      } 
 } 
其中 getField(instance.getClass(), fieldName) 通過反射機制獲得物件屬性,如果存在安全管理器,方法首先使用 this 和 Member.DECLARED 作為引數呼叫安全管理器的 checkMemberAccess 方法,這裡的 this 是 this 類或者成員被確定的父類。 如果該類在包中,那麼方法還使用包名作為引數呼叫安全管理器的 checkPackageAccess 方法。 每一次呼叫都可能導致 SecurityException。當訪問被拒絕時,這兩種呼叫方式都會產生 securityexception 異常 。
setAccessible(true) 方法通過指定引數值為 true 來禁用訪問控制檢查,從而使得該變數可以被其他類呼叫。我們可以在我們所寫的類中,擴充套件一個普通的基本類 java.lang.reflect.AccessibleObject 類。這個類定義了一種 setAccessible 方法,使我們能夠啟動或關閉對這些類中其中一個類的例項的接入檢測。這種方法的問題在於如果使用了安全性管理器,它將檢測正在關閉接入檢測的程式碼是否允許這樣做。如果未經允許,安全性管理器丟擲一個例外。


除訪問私有變數,我們也可以通過這個方法訪問私有方法。

清單 4. 利用反射機制訪問類的成員方法

 public static Method getMethod(Object instance, String methodName, Class[] classTypes) 
       throws    NoSuchMethodException ... {
 
      Method accessMethod = getMethod(instance.getClass(), methodName, classTypes);
      // 引數值為true,禁用訪問控制檢查 
      accessMethod.setAccessible( true );
 
      return accessMethod;
 } 
 
 private static Method getMethod(Class thisClass, String methodName, Class[] classTypes) 
       throws NoSuchMethodException ... {
 
       if (thisClass == null ) ... {
          throw new NoSuchMethodException( " Error method ! " );
       } try ... {
          return thisClass.getDeclaredMethod(methodName, classTypes);
       } catch (NoSuchMethodException e) ... {
          return getMethod(thisClass.getSuperclass(), methodName, classTypes);
             
      } 
 } 

獲得私有方法的原理與獲得私有變數的方法相同。當我們得到了函式後,需要對它進行呼叫,這時我們需要通過 invoke() 方法來執行對該函式的呼叫,程式碼示例如下:
// 呼叫含單個引數的方法 
 public static Object invokeMethod(Object instance, String methodName, Object arg) 
      throws NoSuchMethodException,
       IllegalAccessException, InvocationTargetException ... {
 
      Object[] args = new Object[ 1 ];
      args[ 0 ] = arg;
      return invokeMethod(instance, methodName, args);
 } 
 
 // 呼叫含多個引數的方法 
 public static Object invokeMethod(Object instance, String methodName, Object[] args) 
      throws NoSuchMethodException,
       IllegalAccessException, InvocationTargetException ... {
      Class[] classTypes = null ;
       if (args != null ) ... {
          classTypes = new Class[args.length];
           for ( int i = 0 ; i < args.length; i ++ ) ... {
               if (args[i] != null ) ... {
                  classTypes[i] = args[i].getClass();
              } 
          } 
      } 
      return getMethod(instance, methodName, classTypes).invoke(instance, args);
 } 

利用安全管理器及反射,可以在不修改原始碼的基礎上訪問私有成員,為測試帶來了極大的方便。尤其是在編譯期間,該方法可以順利地通過編譯。但同時該方法也有一些缺點。第一個是效能問題,用於欄位和方法接入時反射要遠慢於直接程式碼。第二個是許可權問題,有些涉及 Java 安全的程式程式碼並沒有修改安全管理器的許可權,此時本方法失效。