LoginSignup
4
0

More than 5 years have passed since last update.

【Android P】Intent.ACTION_UNINSTALL_PACKAGEを飛ばしてアプリをアンインストールする際の注意点

Last updated at Posted at 2018-07-31

概要

アプリ内からボタンイベント等で他のアプリのアンインストールを実行する際にIntent.ACTION_UNINSTALL_PACKAGEを飛ばしますが、この処理を実装しているアプリをAndroid Pで実行した場合、そのままではアンインストール処理が走らないケースがあります。メモ書きとしてここに記します。

権限の追加

Intent.ACTION_UNINSTALL_PACKAGEを使う処理をアプリ内に実装する場合、android.permission.REQUEST_DELETE_PACKAGESをmanifest内で宣言する必要があります。

AndroidManifest.xml
    <uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />

android.permission.REQUEST_DELETE_PACKAGES自体はAPI level 26(Oreo)で実装されたものですが、API level 28以降をtargetSdkVersionとするアプリにおいて、Intent.ACTION_UNINSTALL_PACKAGEの使用に際して必須となりました。

Allows an application to request deleting packages. Apps targeting APIs Build.VERSION_CODES.P or greater must hold this permission in order to use Intent.ACTION_UNINSTALL_PACKAGE or PackageInstaller.uninstall(VersionedPackage, IntentSender).

ここで一点注意しなければいけないのが、"must hold this permission in order to use Intent.ACTION_UNINSTALL_PACKAGE"という部分です。実際に権限を要求するのは内部で使われるUninstallerActivity.classであり、同クラスを使用するIntent.ACTION_DELETEにおいても同様のパーミッション宣言が必要です。

今回の権限追加によるAndroid P対応について、以下で検証してみます。

検証

実行環境はAndroidStudio3.1.3、エミュレータはAPI 27とAPI 28の2つを使用します。

アンインストールの対象となるSampleAppと、Intentを投げてアンインストール処理を呼び出すRemoveAppを作成します。
SampleAppはAndroidStudioが生成した状態のまま使用、RemoveAppにはactivity.mainにRemoveButtonを配置し、MainActivity.javaにてonClickメソッドを実装します。
targetSdkVersionを28に設定します。

activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/remove_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="onClickRemoveButton"
        android:text="REMOVE"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>
MainActivity.java
package jp.co.example.www.removeapp;

import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void onClickRemoveButton(View v) {
        Intent intent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE, Uri.fromParts("package", "jp.co.example.www.sampleapp", null));
        startActivity(intent);
    }
}

まず、manifestにpermissionを宣言しない状態で検証してみます。

API 27における動作

作成した2つのアプリがインストールされた状態で、RemoveApp上でボタンをタップします。
Slide1.jpg
当然ダイアログが表示されます。

API 28における動作

上記と同様、作成した2つのアプリをインストールしRemoveAppをPで実行してみます。
RemoveAppのRemoveボタンをタップします。
Slide1.jpg
何も起きません。ログを見てみましょう。

07-31 12:45:44.775 4372-4372/? E/UninstallerActivity: Uid 10082 does not have android.permission.REQUEST_DELETE_PACKAGES or android.permission.DELETE_PACKAGES

UninstallerActivityがpermissionを要求しています。manifestに書き加えましょう。

AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="jp.co.example.www.removeapp">

    <uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

再び実行してみます。
Slide1.jpg
ばっちり!

ちなみに

上記のエラーログはログキャットのフィルターを「選択したアプリケーションのみ」にしていると出てきません。気付きにくいですね。

4
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
0