DroidPluginTeam / DroidPlugin

A plugin framework on android,Run any third-party apk without installation, modification or repackage
http://droidpluginteam.github.io/DroidPlugin/
GNU Lesser General Public License v3.0
6.89k stars 2.53k forks source link

对使用Alarm Manager的Service的支持有问题 #25

Open wywlds opened 8 years ago

wywlds commented 8 years ago

问题是这样: 我现在有一个Service,为了保持长连,使用Alarm Manager:

        PendingIntent pi = PendingIntent.getService(this, 0, i,
                PendingIntent.FLAG_UPDATE_CURRENT);
        alarmMgr.setRepeating(AlarmManager.RTC_WAKEUP,
                System.currentTimeMillis() + KEEP_ALIVE_INTERVAL,
                KEEP_ALIVE_INTERVAL, pi);

在PendingIntent.getService方法里面会调用到

            IIntentSender target =
                ActivityManagerNative.getDefault().getIntentSender(
                    ActivityManager.INTENT_SENDER_SERVICE, packageName,
                    null, null, requestCode, new Intent[] { intent },
                    resolvedType != null ? new String[] { resolvedType } : null,
                    flags, null, UserHandle.myUserId());

这个方法已经被hook了,我们会把它做替换,在IActivityManagerHookHandle.java

 //这里我们用新的逻辑,将原来的PendingIntent.getXXX(XXX,XXX, Intent, XXX)全部替换成
 //PendingIntent.getService(XXX,XXX,intetn,XXX)
 //这样系统在处理的时候,会先调用到我们的中转服务,我们的中转服务再来处理这个事情。
Intent replaced = replace(type, intent);
        private Intent replace(int type, Intent intent) throws RemoteException {
            if (type == ActivityManagerCompat.INTENT_SENDER_SERVICE) {
                ServiceInfo a = resolveService(intent);
                if (a != null && isPackagePlugin(a.packageName)) {
                    Intent newIntent = new Intent(mHostContext, PluginManagerService.class);
                    newIntent.putExtra(Env.EXTRA_TARGET_INTENT, intent);
                    newIntent.putExtra(Env.EXTRA_TYPE, type);
                    newIntent.putExtra(Env.EXTRA_ACTION, "PendingIntent");
                    return newIntent;
                }
            } else if (type == ActivityManagerCompat.INTENT_SENDER_ACTIVITY) {
                ActivityInfo a = resolveActivity(intent);
                if (a != null && isPackagePlugin(a.packageName)) {
                    Intent newIntent = new Intent(mHostContext, PluginManagerService.class);
                    newIntent.putExtra(Env.EXTRA_TARGET_INTENT, intent);
                    Log.e("wywdbg", "replace intent: %s", intent);
                    newIntent.putExtra(Env.EXTRA_TYPE, type);
                    newIntent.putExtra(Env.EXTRA_ACTION, "PendingIntent");
                    return newIntent;
                }
            }
            return null;
        }

但是在系统的getIntentSender方法中会复用原有的PendingIntent,然后将信息替换成新的,源代码如下:

       PendingIntentRecord.Key key = new PendingIntentRecord.Key(
                type, packageName, activity, resultWho,
                requestCode, intents, resolvedTypes, flags, options, userId);
        WeakReference<PendingIntentRecord> ref;
        ref = mIntentSenderRecords.get(key);
        PendingIntentRecord rec = ref != null ? ref.get() : null;
        if (rec != null) {
            if (!cancelCurrent) {
                if (updateCurrent) {
                    if (rec.key.requestIntent != null) {
                        rec.key.requestIntent.replaceExtras(intents != null ?
                                intents[intents.length - 1] : null);
                    }
                    if (intents != null) {
                        intents[intents.length-1] = rec.key.requestIntent;
                        rec.key.allIntents = intents;
                        rec.key.allResolvedTypes = resolvedTypes;
                    } else {
                        rec.key.allIntents = null;
                        rec.key.allResolvedTypes = null;
                    }
                }
                return rec;
            }
            rec.canceled = true;
            mIntentSenderRecords.remove(key);
        }
        if (noCreate) {
            return rec;
        }
        rec = new PendingIntentRecord(this, key, callingUid);
        mIntentSenderRecords.put(key, rec.ref);
        if (type == ActivityManager.INTENT_SENDER_ACTIVITY_RESULT) {
            if (activity.pendingResults == null) {
                activity.pendingResults
                        = new HashSet<WeakReference<PendingIntentRecord>>();
            }
            activity.pendingResults.add(rec.ref);
        }
        return rec;

由于我们统一换成了先从PluginManagerService中转,这样就会出现已经注册在AlarmManager里面的PendingIntent在这种时候被修改了,导致打开的不是我们想注册打开的service而是后面调用到PendingIntent.getService的其他组件,比如说一个通知栏注册点击事件时的PendingIntent(这种通常是会打开Activity的)。

不知道作者有什么好的解决方案。

wywlds commented 8 years ago

现在倒是有一个办法绕过,app里面每个使用到PendingIntent的地方采用同一套requestCode排重方式。

或者针对不同的Service和Activity使用不同的requestCode的base,这个可以通过修改DroidPlugin来实现。