#概要
アプリ内からボタンイベント等で他のアプリのアンインストールを実行する際にIntent.ACTION_UNINSTALL_PACKAGE
を飛ばしますが、この処理を実装しているアプリをAndroid Pで実行した場合、そのままではアンインストール処理が走らないケースがあります。メモ書きとしてここに記します。
#権限の追加
Intent.ACTION_UNINSTALL_PACKAGE
を使う処理をアプリ内に実装する場合、android.permission.REQUEST_DELETE_PACKAGES
をmanifest内で宣言する必要があります。
<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に設定します。
<?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>
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上でボタンをタップします。
当然ダイアログが表示されます。
##API 28における動作
上記と同様、作成した2つのアプリをインストールしRemoveAppをPで実行してみます。
RemoveAppのRemoveボタンをタップします。
何も起きません。ログを見てみましょう。
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に書き加えましょう。
<?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>
#ちなみに
上記のエラーログはログキャットのフィルターを「選択したアプリケーションのみ」にしていると出てきません。気付きにくいですね。