首先感歎一下android強大,它可以把同一個apk裡面得不同Actvity分別運行在不同的進程中,比如我想讓自己的應用中Activity運行在Phone進程中,那麼我需要做三個事情。
本文包含三個知識點:
1.監聽去電接通 2.apk擷取root許可權 3. 運行時將apk push 到system/app
(1)
[java]
<activity
ndroid:process="com.android.phone"
android:label="@string/app_name"
android:name=".AutoCallActivity" >
</activity>
<activity
android:process="com.android.phone"
android:label="@string/app_name"
android:name=".AutoCallActivity" >
</activity> (2)
[java]
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.spreadst.drag"
coreApp="true"
oid:sharedUserId="android.uid.system"
android:versionCode="1"
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.spreadst.drag"
coreApp="true"
android:sharedUserId="android.uid.system"
android:versionCode="1"
(3) [java]
LOCAL_PACKAGE_NAME := autocall
#LOCAL_CERTIFICATE := shared
LOCAL_CERTIFICATE := platform
LOCAL_PACKAGE_NAME := autocall
#LOCAL_CERTIFICATE := shared
LOCAL_CERTIFICATE := platform 為什麼會有這個需求呢?TelephonyManager 中狀態 只有 IDLE 空閑狀態, 來電接通狀態 ,撥打狀態,沒有去電接通狀態,參考phone中得實現如下:
Phone phone = PhoneFactory.getDefaultPhone();
這個調用是不能運行在phone以外的進程中的,不信你可以試一下,請記住一個進程的主線程的looper是唯一的
原因如下:
[java]
if (sLooper != Looper.myLooper()) {
throw new RuntimeException(
"PhoneFactory.getDefaultPhone must be called from Looper thread");
}
if (sLooper != Looper.myLooper()) {
throw new RuntimeException(
"PhoneFactory.getDefaultPhone must be called from Looper thread");
}去電接通若干秒之後掛斷代碼如下,由於使用了hide class,所以必須在android原始碼底下編譯,當然你也可以通過反射、AIDL、class.jar等方式解決這個問題。
[java]
CallManager mCM = CallManager.getInstance();
Phone phone = PhoneFactory.getDefaultPhone();
mCM.registerPhone(phone);
mCM.registerForPreciseCallStateChanged(mHandler, PHONE_STATE_CHANGED, null);
CallManager mCM = CallManager.getInstance();
Phone phone = PhoneFactory.getDefaultPhone();
mCM.registerPhone(phone);
mCM.registerForPreciseCallStateChanged(mHandler, PHONE_STATE_CHANGED, null);
[java]
private Handler mHandler=new Handler(){
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case PHONE_STATE_CHANGED:
updatePhoneSateChange();
break;
default:
break;
}
};
};
private Handler mHandler=new Handler(){
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case PHONE_STATE_CHANGED:
updatePhoneSateChange();
break;
default:
break;
}
};
};
[java]
private void updatePhoneSateChange(){
Call fgCall = mCM.getActiveFgCall();
if (mCM.hasActiveRingingCall()) {
fgCall = mCM.getFirstActiveRingingCall();
}
final Call.State state = fgCall.getState();
switch (state) {
case IDLE:
break;
case ACTIVE://去電接通
Log.d("yzy","ACTVIE");
final Timer timer = new Timer();
if(mode == Mode.mode2){
timer.schedule(new TimerTask() {
@Override
public void run() {
try {
Log.d("yzy", "endcall()");
mITelephony.endCall();
timer.cancel();
} catch (RemoteException e) {
e.printStackTrace();
}
}
}, holdonDuration * 1000, holdonDuration * 1000);
}
//mITelephony.endCall();
break;
default:
break;
}
}
private void updatePhoneSateChange(){
Call fgCall = mCM.getActiveFgCall();
if (mCM.hasActiveRingingCall()) {
fgCall = mCM.getFirstActiveRingingCall();
}
final Call.State state = fgCall.getState();
switch (state) {
case IDLE:
break;
case ACTIVE://去電接通
Log.d("yzy","ACTVIE");
final Timer timer = new Timer();
if(mode == Mode.mode2){
timer.schedule(new TimerTask() {
@Override
public void run() {
try {
Log.d("yzy", "endcall()");
mITelephony.endCall();
timer.cancel();
} catch (RemoteException e) {
e.printStackTrace();
}
}
}, holdonDuration * 1000, holdonDuration * 1000);
}
//mITelephony.endCall();
break;
default:
break;
}
}
如果你將這個apk 簡單的install 會報告如下錯誤:不存在該共用使用者或簽名不匹配,這是因為[java] view plaincopyprint?android:sharedUserId="android.uid.system"
android:sharedUserId="android.uid.system"必須放到system/app目錄下才能工作。這就由牽扯出了兩個問題,臨時擷取root許可權與運行時拷貝.
其他關鍵代碼:
[java]
private ITelephony mITelephony;
mITelephony = ITelephony.Stub.asInterface(ServiceManager
.getService(Context.TELEPHONY_SERVICE));
mPhoneStateListener = getPhoneStateListener();
((TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE)).listen(
mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
private PhoneStateListener getPhoneStateListener() {
return new PhoneStateListener() {
@Override
public void onCallStateChanged(int state, String incomingNumber) {
try {
Log.d("yzy"," state: "
+ mITelephony.getCallState());
} catch (RemoteException e) {
e.printStackTrace();
}
}
};
}