4

Android アプリケーション フレームワークの開発に関連するチュートリアルを教えてもらえますか? ここでは、アプリ開発ではなく、「アプリケーション フレームワーク」 (Android アーキテクチャの上から 2 番目のレイヤー)について特に話しています。

興味があります:アプリがシステム/フレームワーク API を呼び出した後はどうなりますか? そのアプリがその特定の権限を持っているかどうか、OS はどのように確認しますか? 「アプリケーション フレームワーク」のどのコンポーネントがこのチェックを処理しますか? どのJavaクラスがそれを担当していますか?

これらのJavaクラスで遊んで、観察したいと思います。

PS:許可モデルは「アプリケーションフレームワーク」レイヤーに実装されていると想定しています。私が間違っている場合は修正してください。

4

1 に答える 1

10

私の知る限り、フレームワーク開発のためのリソースは限られています。利用可能なリソースのほとんどは、さまざまなブログやメーリング リストに分散しています。開始するには、オープン ソース プロジェクト サイトsource.android.comをお勧めします。操作方法に関する限られたドキュメントが含まれていますが、少なくともオープン ソース プロジェクトで作業するためのセットアップが提供されます。次に、プラットフォームおよびフレームワーク レベルの開発に関連する公式のメーリング リストがあります。さまざまな ROM プロジェクトには、 Cyanogenmod wikiなどのサイトにも有用な情報がある場合があります。

次に、フレームワークで権限がどのように実装されているかについての特定の質問に答えます。チェックを処理する特定のコンポーネントはありません。フレームワーク内の各サービス プロバイダーは、サービス呼び出しを許可する前にアクセス許可チェックを実行する必要があります。このようなチェックには、システム サーバーのパッケージ マネージャーと Binder IPC メカニズムの 2 つの重要な要素が関係しています。パッケージ マネージャーは、アプリケーションのインストールを処理する OS コンポーネントです。これにより、インストール時に AndroidManifest.xml ファイルが解析され、ユーザーにアクセス許可を求めるプロンプトが表示され、特定のアプリが保持するアクセス許可のレジストリが維持されます。これは、各アプリケーションが独自の Linux ユーザー ID で実行されるという考えに基づいています。各 uid には、パーミッションのリストがあります。

2 つ目の部分は、Binder のプロセス間通信メカニズムです。Binder は、IPC を実行するオブジェクト指向の方法ですが、いくつかのセキュリティ機能も実装しています。アクセス許可に関連する最も重要な点は、IPC 呼び出しの受信側が呼び出し元の uid をチェックできるようにすることです。アクセス許可で保護されているサービスは、Binder インターフェイスを持ち、受信したすべての要求に対して 2 つのことを行います。最初にバインダーを呼び出して呼び出し元の uid を取得し、次にシステム サーバーを呼び出して、uid と許可されているかどうかを確認する許可を提供します。チェックが OK の場合、続行してサービス コールを実行します。それ以外の場合は、セキュリティ例外が発生します。

ソースコードを見ると、バイブレーター サービスへの単純な呼び出しから始まります。(以下のすべてのコードは、Apache 2.0 ライセンスに基づく Android オープン ソース プロジェクトの著作権です)。

public void vibrate(long milliseconds, IBinder token) {
    if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE)
            != PackageManager.PERMISSION_GRANTED) {
        throw new SecurityException("Requires VIBRATE permission");
    }

フレームワーク レベルのパーミッション チェックの実装は Context クラスに属し、具体的には ContextImpl.java ファイルがあります。

@Override
public int checkCallingOrSelfPermission(String permission) {
    if (permission == null) {
        throw new IllegalArgumentException("permission is null");
    }

    return checkPermission(permission, Binder.getCallingPid(),
            Binder.getCallingUid());
}
@Override
public int checkPermission(String permission, int pid, int uid) {
    if (permission == null) {
        throw new IllegalArgumentException("permission is null");
    }

    try {
        return ActivityManagerNative.getDefault().checkPermission(
                permission, pid, uid);
    } catch (RemoteException e) {
        return PackageManager.PERMISSION_DENIED;
    }
}

これは、Binder を介した ActivityManagerService への呼び出しであり、最終的には次のようになります。

/**
 * As the only public entry point for permissions checking, this method
 * can enforce the semantic that requesting a check on a null global
 * permission is automatically denied.  (Internally a null permission
 * string is used when calling {@link #checkComponentPermission} in cases
 * when only uid-based security is needed.)
 * 
 * This can be called with or without the global lock held.
 */
public int checkPermission(String permission, int pid, int uid) {
    if (permission == null) {
        return PackageManager.PERMISSION_DENIED;
    }
    return checkComponentPermission(permission, pid, uid, -1, true);
} 
/**
 * This can be called with or without the global lock held.
 */
int checkComponentPermission(String permission, int pid, int uid,
        int owningUid, boolean exported) {
    // We might be performing an operation on behalf of an indirect binder
    // invocation, e.g. via {@link #openContentUri}.  Check and adjust the
    // client identity accordingly before proceeding.
    Identity tlsIdentity = sCallerIdentity.get();
    if (tlsIdentity != null) {
        Slog.d(TAG, "checkComponentPermission() adjusting {pid,uid} to {"
                + tlsIdentity.pid + "," + tlsIdentity.uid + "}");
        uid = tlsIdentity.uid;
        pid = tlsIdentity.pid;
    }

    // Root, system server and our own process get to do everything.
    if (uid == 0 || uid == Process.SYSTEM_UID || pid == MY_PID) {
        return PackageManager.PERMISSION_GRANTED;
    }
    // If there is a uid that owns whatever is being accessed, it has
    // blanket access to it regardless of the permissions it requires.
    if (owningUid >= 0 && uid == owningUid) {
        return PackageManager.PERMISSION_GRANTED;
    }
    // If the target is not exported, then nobody else can get to it.
    if (!exported) {
        Slog.w(TAG, "Permission denied: checkComponentPermission() owningUid=" + owningUid);
        return PackageManager.PERMISSION_DENIED;
    }
    if (permission == null) {
        return PackageManager.PERMISSION_GRANTED;
    }
    try {
        return AppGlobals.getPackageManager()
                .checkUidPermission(permission, uid);
    } catch (RemoteException e) {
        // Should never happen, but if it does... deny!
        Slog.e(TAG, "PackageManager is dead?!?", e);
    }
    return PackageManager.PERMISSION_DENIED;
}

パッケージ マネージャー checkUidPermission への呼び出しは、ルックアップを実行して uid を付与されたアクセス許可のテーブルと照合するものです。ソースのトレースを続行する場合、関連するファイルは PackageManagerService.java です。

調査を行っているだけの場合は、オープン ソース プロジェクトの Frameworks/base/ のコードに飛び込んでください。上記のすべてのファイルがそこにあります。ビルド手順に従うと、エミュレーターを使用して変更をテストできるはずです。コア フレームワーク ファイル自体を変更したくない場合は、/device/sample にあるサンプルを参照して、フレームワークの拡張方法を確認してください。つまり、パーミッション関連の API のほとんどはアプリケーション レベルから利用できるため、サービスを提供するアプリケーションを用意し、それに対して独自のパーミッション チェックを行うだけで成功する可能性があります。

于 2012-01-25T13:26:06.440 に答える