android11.0(R) 谷歌浏览器去除短信分享功能

简介: android11.0(R) 谷歌浏览器去除短信分享功能

客户定制


GMS 版本下,要求去除谷歌浏览器中分享菜单下短信备选项,最终实现效果如下


oluyvR.png要输入网页后右上角菜单中才会显示分享功能。


解决办法


frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

@@ -7413,11 +7417,44 @@ public class PackageManagerService extends IPackageManager.Stub
         if (sortResult) {
             Collections.sort(result, RESOLVE_PRIORITY_SORTER);
         }
+       
+        android.util.Log.i("LogUtils", "step 3");
+        if (result!=null) {
+               android.util.Log.i("LogUtils", "step 4=="+result.size());//cczheng
+        }
+        if ("android.intent.action.SEND".equals(intent.getAction())) {
+               blockGoogleMessagingShare(result);
+        }
         return applyPostResolutionFilter(
                 result, instantAppPkgName, allowDynamicSplits, filterCallingUid, resolveForStart,
                 userId, intent);
     }
+    //cczheng add 
+    private void blockGoogleMessagingShare(List<ResolveInfo> result){
+       android.util.Log.d("LogUtils", "into blockGoogleMessagingShare");
+       ActivityManager am = (ActivityManager)mContext.getSystemService(Context.ACTIVITY_SERVICE);
+        List<ActivityManager.RunningTaskInfo> tasks = am.getRunningTasks(1);
+        if (!tasks.isEmpty()) {
+            ComponentName topActivity = tasks.get(0).topActivity;
+            String currentPackageName = topActivity.getPackageName();
+            if ("com.android.chrome".equals(currentPackageName)) {
+                for (int i = 0; i < result.size(); i++) {
+                           ResolveInfo resolveInfo = result.get(i);
+                           android.util.Log.i("LogUtils", "resolveInfoCount="+resolveInfo.toString());
+                           if ("com.google.android.apps.messaging".equals(resolveInfo.activityInfo.packageName)){
+                               result.remove(i);
+                           }
+                       }
+                android.util.Log.e("LogUtils", "step 4=="+result.size());
+            }
+        }
+    }//end
+
     private List<ResolveInfo> maybeAddInstantAppInstaller(List<ResolveInfo> result, Intent intent,
             String resolvedType, int flags, int userId, boolean resolveForStart,
             boolean isRequesterInstantApp) {

frameworks/base/core/java/com/android/internal/app/ChooserActivity.java

@@ -588,6 +588,26 @@ public class ChooserActivity extends ResolverActivity implements
         }
     };
+    //cchzeng add start
+    private String getLastPausedActivity() {
+        String result = "";
+        try {
+            java.lang.Process p = java.lang.Runtime.getRuntime().exec("dumpsys activity activities  grep mLastPausedActivity ");
+            java.io.InputStream input = p.getInputStream();
+            java.io.BufferedReader in = new java.io.BufferedReader(new java.io.InputStreamReader(input));
+            StringBuffer stringBuffer = new StringBuffer();
+            String content = "";
+            while ((content = in.readLine()) != null) {
+                stringBuffer.append(content);
+            }
+            result = stringBuffer.toString();
+            int status = p.waitFor();
+        } catch (Exception e) {
+           e.printStackTrace();
+        }
+        return result;
+    }//cczheng add end
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         final long intentReceivedTime = System.currentTimeMillis();
@@ -692,7 +712,26 @@ public class ChooserActivity extends ResolverActivity implements
         pa = intent.getParcelableArrayExtra(Intent.EXTRA_EXCLUDE_COMPONENTS);
-
+        //cczheng add start
+        String lastPausedActivityStr = getLastPausedActivity();
+        Log.d(TAG,"ResolverActivity onCreate  "+lastPausedActivityStr);
+        try {
+         if (!TextUtils.isEmpty(lastPausedActivityStr)) {
+            lastPausedActivityStr = lastPausedActivityStr.substring(lastPausedActivityStr.indexOf("mLastPausedActivity"),
+                lastPausedActivityStr.indexOf("* Task"));
+            Log.d(TAG,"lastPausedActivityStr=  "+lastPausedActivityStr);
+            if (lastPausedActivityStr.contains("com.android.chrome")) {
+                android.provider.Settings.System.putInt(getContentResolver(), "tempflag", 1);
+            }else{
+                android.provider.Settings.System.putInt(getContentResolver(), "tempflag", 0);
+            }
+         }else{
+            android.provider.Settings.System.putInt(getContentResolver(), "tempflag", 0);
+         }
+        } catch (Exception e) {
+           e.printStackTrace();
+        } //cczheng add end
+        
         // Exclude out Nearby from main list if chip is present, to avoid duplication
         ComponentName nearbySharingComponent = getNearbySharingComponent();
         boolean hasNearby = nearbySharingComponent != null;

frameworks/base/core/java/com/android/internal/app/ResolverActivity.java

@@ -139,7 +139,7 @@ public class ResolverActivity extends Activity implements
+    //cchzeng add start
+     private String getLastPausedActivity() {
+        String result = "";
+        try {
+            java.lang.Process p = java.lang.Runtime.getRuntime().exec("dumpsys activity activities  grep mLastPausedActivity ");
+            java.io.InputStream input = p.getInputStream();
+            java.io.BufferedReader in = new java.io.BufferedReader(new java.io.InputStreamReader(input));
+            StringBuffer stringBuffer = new StringBuffer();
+            String content = "";
+            while ((content = in.readLine()) != null) {
+                stringBuffer.append(content);
+            }
+            result = stringBuffer.toString();
+            int status = p.waitFor();
+        } catch (Exception e) {
+           e.printStackTrace();
+        }
+        return result;
+    }//cczheng add end
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         // Use a specialized prompt when we're handling the 'Home' app startActivity()
@@ -321,6 +341,26 @@ public class ResolverActivity extends Activity implements
             mResolvingHome = true;
         }
+        //cczheng add start
+        String lastPausedActivityStr = getLastPausedActivity();
+        Log.d(TAG,"ResolverActivity onCreate  "+lastPausedActivityStr);
+        try {
+         if (!TextUtils.isEmpty(lastPausedActivityStr)) {
+            lastPausedActivityStr = lastPausedActivityStr.substring(lastPausedActivityStr.indexOf("mLastPausedActivity"),
+                lastPausedActivityStr.indexOf("* Task"));
+            Log.d(TAG,"lastPausedActivityStr=  "+lastPausedActivityStr);
+            if (lastPausedActivityStr.contains("com.android.chrome")) {
+                android.provider.Settings.System.putInt(getContentResolver(), "tempflag", 1);
+            }else{
+                android.provider.Settings.System.putInt(getContentResolver(), "tempflag", 0);
+            }
+          }else{
+            android.provider.Settings.System.putInt(getContentResolver(), "tempflag", 0);
+          }
+        } catch (Exception e) {
+           e.printStackTrace();
+        } //cczheng add end
+
         setSafeForwardingMode(true);
         onCreate(savedInstanceState, intent, null, 0, null, null, true);

frameworks/base/core/java/com/android/internal/app/ResolverListAdapter.java

@@ -433,6 +433,16 @@ public class ResolverListAdapter extends BaseAdapter {
     // Check whether {@code dri} should be added into mDisplayList.
     protected boolean shouldAddResolveInfo(DisplayResolveInfo dri) {
+        //cczheng add start
+        int flag =android.provider.Settings.System.getInt(mContext.getContentResolver(), "tempflag", 0);
+        if (flag == 1) {
+            String driPackageName = dri.getResolvedComponentName().getPackageName();
+             Log.d(TAG, "shouldAddResolveInfo driPackageName: " + driPackageName);
+            if ("com.google.android.apps.messaging".equals(driPackageName)) {
+               return false;
+            }
+        }
+        //cczheng add end
         // Checks if this info is already listed in display.
         for (DisplayResolveInfo existingInfo : mDisplayList) {
             if (mResolverListCommunicator


分析过程


一开始有点懵,毕竟没有 message app 的源码,一时不知道从哪里下手,要想完成这个需求,毋庸置疑要改 framework

先看了下 message app 的 AndroidManifest.xml,在其中搜到了 send 相关的字样,然后自己新建个 demo,把配置

<intent-filter >
  <action android:name="android.intent.action.SEND" />
  <category android:name="android.intent.category.DEFAULT" />
  <data android:mimeType="text/plain" />
</intent-filter>

copy 了一份安装后发现 demo 也出现在分享列表里了,这么说就是和 android.intent.action.SEND 这个东东相关了。


按照惯例看下这个分享页面到底是系统界面还是 chrome 界面,得到结论


点 share 第一次弹出界面为 chrome 界面,com.android.chrome/com.google.android.apps.chrome.Main


点 more 按钮二次显示界面为系统界面,android/com.android.internal.app.ChooserActivity


这样看来两个地方走的逻辑不太一样,我们先从简单的系统界面入手,直接去找 ChooserActivity 源码


找源码之前我们要学会分析 log, 因为 log 中经常会藏有很多有用信息,我们要过滤的 message 包名


为 com.google.android.apps.messaging,在 log 中我发现了匹配的串

I/ResolverListAdapter: Add DisplayResolveInfo component: ComponentInfo{com.google.android.apps.messaging/com.google.android.apps.messaging.ui.conversationlist.ShareIntentActivity}, intent component: ComponentInfo{com.google.android.apps.messaging/com.google.android.apps.messaging.ui.conversationlist.ShareIntentActivity}
I/ResolverListAdapter: Add DisplayResolveInfo component: ComponentInfo{com.android.chrome/org.chromium.chrome.browser.printing.PrintShareActivity}, intent component: ComponentInfo{com.android.chrome/org.chromium.chrome.browser.printing.PrintShareActivity}
I/ResolverListAdapter: Add DisplayResolveInfo component: ComponentInfo{com.yandex.browser/com.yandex.browser.ShareActivity}, intent component: ComponentInfo{com.yandex.browser/com.yandex.browser.ShareActivity}
I/ResolverListAdapter: Add DisplayResolveInfo component: ComponentInfo{com.hj119.sygjx/com.e4a.runtime.components.impl.android.hjfzdjtb类库.CopyToClipboard}, intent component: ComponentInfo{com.hj119.sygjx/com.e4a.runtime.components.impl.android.hjfzdjtb类库.CopyToClipboard}
I/ResolverListAdapter: Add DisplayResolveInfo component: ComponentInfo{com.android.bluetooth/com.android.bluetooth.opp.BluetoothOppLauncherActivity}, intent component: ComponentInfo{com.android.bluetooth/com.android.bluetooth.opp.BluetoothOppLauncherActivity}
I/ResolverListAdapter: Add DisplayResolveInfo component: ComponentInfo{com.google.android.gm/com.google.android.gm.ComposeActivityGmailExternal}, intent component: ComponentInfo{com.google.android.gm/com.google.android.gm.ComposeActivityGmailExternal}
I/ResolverListAdapter: Add DisplayResolveInfo component: ComponentInfo{com.google.android.apps.docs/com.google.android.apps.docs.common.shareitem.UploadMenuActivity}, intent component: ComponentInfo{com.google.android.apps.docs/com.google.android.apps.docs.common.shareitem.UploadMenuActivity}

仔细一看发现和 ChooserActivity 界面显示 icon 个数正好对上,那岂不是找对地方了。看了源码发现 ChooserActivity 继承 ResolverActivity,


通过 ResolverListAdapter 填充数据,所以我们只需要去 Add DisplayResolveInfo component 打印地方将 message 过滤即可。


但这里有个麻烦点,并不是所有应用调用 ChooserActivity 时都过滤 message ,客户只指定了 chrome,所以我们还需要知道是从哪个 app 中拉起的系统 ChooserActivity


这个一开始在找各种 api 是否能知道是谁拉起的 Activity,后来通过巧妙的方法来达到了要求,秘密就是


adb shell dumpsys activity activities | grep mLastPausedActivity 指令,查询到 ActivityRecord 中上一个 pause 的 Activity 不就是调用者。


mLastPausedActivity: ActivityRecord{ae7cc5b u0 com.android.chrome/com.google.android.apps.chrome.Main t83}


mLastPausedActivity: ActivityRecord{1fcfbc6 u0 com.android.launcher3/com.android.searchlauncher.SearchLauncher t6}


java 代码中执行 dumpsys activity activities | grep mLastPausedActivity 实际并没有过滤到和命令行中执行一样的结果,


而是返回完整串需要 subString 处理一下,通过 Settings.System.putInt 存储 mLastPausedActivity 结果临时值


在 ResolverListAdapter 中取出进行过滤。


通过以下代码可以拉起 ChooserActivity/ResolverActivity


        Intent intentss = new Intent();
        intentss.setAction(Intent.ACTION_SEND);
        intentss.putExtra(Intent.EXTRA_TEXT, "文本内容");
        intentss.setType("text/plain");
        //startActivity(intentss);//android/com.android.internal.app.ResolverActivity
        startActivity(Intent.createChooser(intentss, "share title"));//android/com.android.internal.app.ChooserActivity

简单的搞定了,接下来就是 chrome 里这个界面了,这个界面有点难顶,前后找了几天,最终发现了 ShareActionProvider 这货


使用ShareActionProvider分享数据


同样整了个简单 demo 跑起来发现和 chrome 里显示列表一毛一样,简单代码如下


res/文件夹下新建 menu 文件夹,新增 share.xml 文件


<!--androidX 版本-->
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <item
        android:id="@+id/menu_item_share"
        app:showAsAction="ifRoom"
        app:actionProviderClass="androidx.appcompat.widget.ShareActionProvider"
        android:title="Share" />
</menu>
<!--android低版本-->
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:id="@+id/menu_item_share"
        android:showAsAction="ifRoom"
        android:actionProviderClass="android.widget.ShareActionProvider"
        android:title="share"/>
</menu>

注意看到一个是 androidx 一个是默认 widget 包里面的 ShareActionProvider,widget 包中的源码是存在 aosp 里的


androidX 中的都是库,很不幸 chrome 就是用的 androidX


随便新建一个 Activity 主题要求带 ActionBar,这样右上角才能添加 menu 菜单


<!--androidX 版本-->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!--android低版本-->
android:theme="@android:style/Theme.Holo.Light.DarkActionBar"

在 Activity 中添加 share menu 功能

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.share, menu);
        MenuItem item = menu.findItem(R.id.menu_item_share);
    //androidX 版本
        ShareActionProvider mShareActionProvider = (ShareActionProvider) MenuItemCompat.getActionProvider(item);
    //android低版本
    //ShareActionProvider mShareActionProvider = (ShareActionProvider)item.getActionProvider();
        Intent shareIntent = new Intent();
        shareIntent.setAction(Intent.ACTION_SEND);
        shareIntent.setType("text/plain");
        shareIntent.putExtra(Intent.EXTRA_TEXT,"aaaaa");
        mShareActionProvider.setShareIntent(shareIntent);
        return true;
    }


这样在 demo 中点击右上角分享图标就能显示一样的分享列表数据

好了主角登场,跟进 ShareActionProvider.java 中

@Override
    public void onPrepareSubMenu(SubMenu subMenu) {
        // Clear since the order of items may change.
        subMenu.clear();
        ActivityChooserModel dataModel = ActivityChooserModel.get(mContext, mShareHistoryFileName);
        PackageManager packageManager = mContext.getPackageManager();
        final int expandedActivityCount = dataModel.getActivityCount();
        final int collapsedActivityCount = Math.min(expandedActivityCount, mMaxShownActivityCount);
        // Populate the sub-menu with a sub set of the activities.
        for (int i = 0; i < collapsedActivityCount; i++) {
            ResolveInfo activity = dataModel.getActivity(i);
            subMenu.add(0, i, i, activity.loadLabel(packageManager))
                    .setIcon(activity.loadIcon(packageManager))
                    .setOnMenuItemClickListener(mOnMenuItemClickListener);
        }
        if (collapsedActivityCount < expandedActivityCount) {
            // Add a sub-menu for showing all activities as a list item.
            SubMenu expandedSubMenu = subMenu.addSubMenu(Menu.NONE, collapsedActivityCount,
                    collapsedActivityCount,
                    mContext.getString(R.string.abc_activity_chooser_view_see_all));
            for (int i = 0; i < expandedActivityCount; i++) {
                ResolveInfo activity = dataModel.getActivity(i);
                expandedSubMenu.add(0, i, i, activity.loadLabel(packageManager))
                        .setIcon(activity.loadIcon(packageManager))
                        .setOnMenuItemClickListener(mOnMenuItemClickListener);
            }
        }
    }

dataModel.getActivityCount() 关键信息,按字面展开 Activity 个数,下面 for 循环依次添加 icon 项

具体怎么获取 count? 跟进 ActivityChooserModel 中 mActivities

/**
     * Loads the activities for the current intent if needed which is
     * if they are not already loaded for the current intent.
     *
     * @return Whether loading was performed.
     */
    private boolean loadActivitiesIfNeeded() {
        if (mReloadActivities && mIntent != null) {
            mReloadActivities = false;
            mActivities.clear();
            List<ResolveInfo> resolveInfos = mContext.getPackageManager()
                    .queryIntentActivities(mIntent, 0);
            final int resolveInfoCount = resolveInfos.size();
            for (int i = 0; i < resolveInfoCount; i++) {
                ResolveInfo resolveInfo = resolveInfos.get(i);
                mActivities.add(new ActivityResolveInfo(resolveInfo));
            }
            return true;
        }
        return false;
    }


可以看到这个 mActivities.add 要是旧版本的 ShareActionProvider 我们在此处进行过滤应该就能生效


但 chrome 用的 androidX 库,只能从 queryIntentActivities() 入手,通过简单模拟 shareIntent


下面这段代码应该就是查询 share 列表代码

    Intent shareIntent = new Intent();
        shareIntent.setAction(Intent.ACTION_SEND);
        shareIntent.setType("text/plain");
        List<ResolveInfo> resolveInfos = getPackageManager().queryIntentActivities(shareIntent, 0);
        String action = shareIntent.getAction();
        final int resolveInfoCount = resolveInfos.size();
        LogUtils.d("resolveInfoCount="+resolveInfoCount + " action="+action);
        for (int i = 0; i < resolveInfos.size(); i++) {
            ResolveInfo resolveInfo = resolveInfos.get(i);
            LogUtils.d("resolveInfoCount="+resolveInfo.toString());
            LogUtils.i("resolveInfoCount=ResolveInfo{a8238ee "+resolveInfo.activityInfo.packageName+"/"+resolveInfo.activityInfo.name);
        }


看到 log 打印正好是分享界面显示的数据,那很明显了需要去 PackageManagerService 中做文章


同样也需要知道是谁调用了 queryIntentActivities 方法,获取当前任务栈中顶部 Activity 包名


ActivityManager.getRunningTasks(1) 如果是 chrome 则过滤 message


queryIntentActivitiesInternal() 有很多地方调用,log 会打印会多,通过多次过滤分析,点击分享瞬间


传递 action 为 android.intent.action.SEND,只有当 action 为 SEND 时才需要过滤,通过上面 demo


调用代码也能发现实际上就是 android.intent.action.SEND 决定的。

     @Override
        public List<ResolveInfo> queryIntentActivities(
                Intent intent, String resolvedType, int flags, int filterCallingUid, int userId) {
            return PackageManagerService.this
                    .queryIntentActivitiesInternal(intent, resolvedType, flags, 0, filterCallingUid,
                            userId, false /*resolveForStart*/, true /*allowDynamicSplits*/);
        }
    private @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent,
            String resolvedType, int flags, @PrivateResolveFlags int privateResolveFlags,
            int filterCallingUid, int userId, boolean resolveForStart, boolean allowDynamicSplits) {
        if (!mUserManager.exists(userId)) return Collections.emptyList();
        final String instantAppPkgName = getInstantAppPackageName(filterCallingUid);
        mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
                false /* requireFullPermission */, false /* checkShell */,
                "query intent activities");
        final String pkgName = intent.getPackage();//cczheng
         String action = intent.getAction();
        android.util.Log.d("LogUtils", "instantAppPkgName="+instantAppPkgName+" resolveInfoCount="+pkgName + " action="+action);
        ComponentName comp = intent.getComponent();
        if (comp == null) {
            if (intent.getSelector() != null) {
                intent = intent.getSelector();
                comp = intent.getComponent();
            }
        }
    ....
    android.util.Log.i("LogUtils", "step 3");
        if (result!=null) {
          android.util.Log.i("LogUtils", "step 4=="+result.size());//cczheng
        }
        if ("android.intent.action.SEND".equals(intent.getAction())) {
          blockGoogleMessagingShare(result);
        }
        return applyPostResolutionFilter(
                result, instantAppPkgName, allowDynamicSplits, filterCallingUid, resolveForStart,
                userId, intent);
    }

关于 init.rc 启动可以参考下面


Android Q 开机启动流程

Android的init过程(二):初始化语言(init.rc)解析


目录
相关文章
|
3月前
|
移动开发 小程序 API
微信外部浏览器或短信链接唤起微信小程序的解决方案
微信外部浏览器或短信链接唤起微信小程序的解决方案
599 1
|
3月前
|
Web App开发 小程序 前端开发
【产品上新】小程序新内核来了!提升安卓浏览器性能,支持WebRTC
【产品上新】小程序新内核来了!提升安卓浏览器性能,支持WebRTC
92 10
|
8天前
|
Web App开发
Chrome——谷歌浏览器chrome如何模拟其他客户端
Chrome——谷歌浏览器chrome如何模拟其他客户端
24 1
Chrome——谷歌浏览器chrome如何模拟其他客户端
|
3月前
|
Web App开发 JavaScript
Vue 项目中使用 debugger 在 chrome 谷歌浏览器中失效以及 console.log 指向去了 vue.js 代码
Vue 项目中使用 debugger 在 chrome 谷歌浏览器中失效以及 console.log 指向去了 vue.js 代码
619 0
|
13天前
|
Linux iOS开发 MacOS
谷歌浏览器中的谷歌翻译失效了?如何解决谷歌翻译不响应问题?
本文分析了谷歌翻译在谷歌浏览器中失效的原因,并提供了针对Mac OS、Windows和Linux系统的解决方案,包括下载和执行特定软件以修复翻译服务不响应的问题。
41 0
谷歌浏览器中的谷歌翻译失效了?如何解决谷歌翻译不响应问题?
|
3月前
|
监控 前端开发 JavaScript
深入探索谷歌浏览器开发者工具:提升网页开发与调试效率的终极指南(一)
深入探索谷歌浏览器开发者工具:提升网页开发与调试效率的终极指南(一)
|
1天前
|
存储 缓存 前端开发
前端谷歌浏览器面版属性
【8月更文挑战第19天】前端谷歌浏览器面版属性
6 0
|
1月前
解除谷歌浏览器默认禁止音频自动播放
解除谷歌浏览器默认禁止音频自动播放
48 1
|
2月前
|
Web App开发 IDE Java
自动化测试谷歌浏览器和其驱动版本差不多却还是报错The chromedriver version (121.0.6167.184) detected in PATH at DPythonchromed
自动化测试谷歌浏览器和其驱动版本差不多却还是报错The chromedriver version (121.0.6167.184) detected in PATH at DPythonchromed
87 2
|
1月前
|
Web App开发
软件开发常见流程之移动端调试方法,利用Chrome(谷歌浏览器)的模拟手机调试,搭建本地Web服务器,手机和服务器在一个局域网,通过手机访问服务器,使用服务器,利用ip实现域名访问
软件开发常见流程之移动端调试方法,利用Chrome(谷歌浏览器)的模拟手机调试,搭建本地Web服务器,手机和服务器在一个局域网,通过手机访问服务器,使用服务器,利用ip实现域名访问