安卓應用安全指南4.6.1處理檔案示例程式碼

apachecn_飛龍發表於2018-03-22

安卓應用安全指南 4.6.1 處理檔案 示例程式碼

原書:Android Application Secure Design/Secure Coding Guidebook

譯者:飛龍

協議:CC BY-NC-SA 4.0

如上所述,檔案原則上應該是私有的。 但是,由於某些原因,有時檔案應該由其他應用直接讀寫。 按照安全形度分類和比較中檔案型別如表 4.6-1 所示。 它們根據檔案儲存位置或其他應用的訪問許可權分為四類。 下面展示了每個檔案類別的示例程式碼,並在其中新增了每個的解釋。

表 4.6-1 按照安全形度的檔案類別和比較

檔案類別 其它應用的訪問許可權 儲存位置 概述
私有檔案 NA 應用目錄中 (1)只能在應用中讀寫,(2)可以處理敏感資料,(3)檔案原則上應該是這個型別
只讀公共檔案 應用目錄中 (1)其它應用和使用者可讀,(2)可以處理公開給應用外部的資訊
讀寫公共檔案 讀寫 應用目錄中 (1)其它應用和使用者可以讀寫,(2)從安全和應用設計角度來看,不應該使用
外部儲存裝置(讀寫檔案) 讀寫 外部儲存裝置,例如 SD 卡 (1)沒有訪問控制,(2)其它應用和使用者總是可以讀寫或刪除檔案,(3)應該以最小需求使用,(4)可以處理很大的檔案

4.6.1.1 使用私有檔案

這種情況下使用的檔案,只能在同一個應用中讀取/寫入,並且這是使用檔案的一種非常安全的方式。 原則上,無論儲存在檔案中的資訊是否是公開的,儘可能使用私有檔案,當與其他應用交換必要的資訊時,應該使用另一個 Android 系統(內容供應器,服務)來完成。

要點:

1) 檔案必須在應用目錄中建立。

2) 檔案的訪問許可權必須設定為私有模式,以免其他應用使用。

3) 可以儲存敏感資訊。

4) 對於儲存在檔案中的資訊,請仔細和安全地處理檔案資料。

PrivateFileActivity.java

package org.jssec.android.file.privatefile;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
public class PrivateFileActivity extends Activity {
    private TextView mFileView;
    private static final String FILE_NAME = "private_file.dat";

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.file);
        mFileView = (TextView) findViewById(R.id.file_view);
    }

    /**
    * Create file process
    *
    * @param view
    */
    public void onCreateFileClick(View view) {
        FileOutputStream fos = null;
        try {
            // *** POINT 1 *** Files must be created in application directory.
            // *** POINT 2 *** The access privilege of file must be set private mode in order not to be used by other applications.
            fos = openFileOutput(FILE_NAME, MODE_PRIVATE);
            // *** POINT 3 *** Sensitive information can be stored.
            // *** POINT 4 *** Regarding the information to be stored in files, handle file data carefully and securely.
            // Omitted, since this is a sample. Please refer to "3.2 Handling Input Data Carefully and Securely."
            fos.write(new String("Not sensotive information (File Activity)¥n").getBytes());
        } catch (FileNotFoundException e) {
            mFileView.setText(R.string.file_view);
        } catch (IOException e) {
            android.util.Log.e("PrivateFileActivity", "failed to read file");
        } finally {
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    android.util.Log.e("PrivateFileActivity", "failed to close file");
                }
            }
        }
        finish();
    }

    /**
    * Read file process
    *
    * @param view
    */
    public void onReadFileClick(View view) {
        FileInputStream fis = null;
        try {
            fis = openFileInput(FILE_NAME);
            byte[] data = new byte[(int) fis.getChannel().size()];
            fis.read(data);
            String str = new String(data);
            mFileView.setText(str);
        } catch (FileNotFoundException e) {
            mFileView.setText(R.string.file_view);
        } catch (IOException e) {
            android.util.Log.e("PrivateFileActivity", "failed to read file");
        } finally {
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    android.util.Log.e("PrivateFileActivity", "failed to close file");
                }
            }
        }
    }

    /**
    * Delete file process
    *
    * @param view
    */
    public void onDeleteFileClick(View view) {
        File file = new File(this.getFilesDir() + "/" + FILE_NAME);
        file.delete();
        mFileView.setText(R.string.file_view);
    }
}

PrivateUserActivity.java

package org.jssec.android.file.privatefile;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;

public class PrivateUserActivity extends Activity {

    private TextView mFileView;
    private static final String FILE_NAME = "private_file.dat";

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.user);
        mFileView = (TextView) findViewById(R.id.file_view);
    }

    private void callFileActivity() {
        Intent intent = new Intent();
        intent.setClass(this, PrivateFileActivity.class);
        startActivity(intent);
    }

    /**
    * Call file Activity process
    *
    * @param view
    */
    public void onCallFileActivityClick(View view) {
        callFileActivity();
    }

    /**
    * Read file process
    *
    * @param view
    */
    public void onReadFileClick(View view) {
        FileInputStream fis = null;
        try {
            fis = openFileInput(FILE_NAME);
            byte[] data = new byte[(int) fis.getChannel().size()];
            fis.read(data);
            // *** POINT 4 *** Regarding the information to be stored in files, handle file data carefully and securely.
            // Omitted, since this is a sample. Please refer to "3.2 Handling Input Data Carefully and Securely."
            String str = new String(data);
            mFileView.setText(str);
        } catch (FileNotFoundException e) {
            mFileView.setText(R.string.file_view);
        } catch (IOException e) {
            android.util.Log.e("PrivateUserActivity", "failed to read file");
        } finally {
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    android.util.Log.e("PrivateUserActivity", "failed to close file");
                }
            }
        }
    }

    /**
    * Rewrite file process
    *
    * @param view
    */
    public void onWriteFileClick(View view) {
        FileOutputStream fos = null;
        try {
            // *** POINT 1 *** Files must be created in application directory.
            // *** POINT 2 *** The access privilege of file must be set private mode in order not to be used by other applications.
            fos = openFileOutput(FILE_NAME, MODE_APPEND);
            // *** POINT 3 *** Sensitive information can be stored.
            // *** POINT 4 *** Regarding the information to be stored in files, handle file data carefully and securely.
            // Omitted, since this is a sample. Please refer to "3.2 Handling Input Data Carefully and Securely."
            fos.write(new String("Sensitive information (User Activity)¥n").getBytes());
        } catch (FileNotFoundException e) {
            mFileView.setText(R.string.file_view);
        } catch (IOException e) {
            android.util.Log.e("PrivateUserActivity", "failed to read file");
        } finally {
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    android.util.Log.e("PrivateUserActivity", "failed to close file");
                }
            }
        }
        callFileActivity();
    }
}

4.6.1.2 使用公共只讀檔案

這是使用檔案向未指定的大量應用公開內容的情況。 如果通過遵循以下幾點來實現,那麼它也是比較安全的檔案使用方法。 請注意,在 API 級別 1 7及更高版本中,不推薦使用MODE_WORLD_READABLE變數來建立公共檔案,並且在 API 級別 24 及更高版本中,會觸發安全異常; 因此使用內容供應器的檔案共享方法更可取。

要點:

1) 檔案必須在應用目錄中建立。

2) 檔案的訪問許可權必須設定為其他應用只讀。

3) 敏感資訊不得儲存。

4) 對於要儲存在檔案中的資訊,請仔細和安全地處理檔案資料。

PublicFileActivity.java

package org.jssec.android.file.publicfile.readonly;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;

public class PublicFileActivity extends Activity {

    private TextView mFileView;
    private static final String FILE_NAME = "public_file.dat";

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.file);
        mFileView = (TextView) findViewById(R.id.file_view);
    }

    /**
    * Create file process
    *
    * @param view
    */
    public void onCreateFileClick(View view) {
        FileOutputStream fos = null;
        try {
            // *** POINT 1 *** Files must be created in application directory.
            // *** POINT 2 *** The access privilege of file must be set to read only to other applications.
            // (MODE_WORLD_READABLE is deprecated API Level 17,
            // don`t use this mode as much as possible and exchange data by using ContentProvider().)
            fos = openFileOutput(FILE_NAME, MODE_WORLD_READABLE);
            // *** POINT 3 *** Sensitive information must not be stored.
            // *** POINT 4 *** Regarding the information to be stored in files, handle file data carefully and securely.
            // Omitted, since this is a sample. Please refer to "3.2 Handling Input Data Carefully and Securely."
            fos.write(new String("Not sensitive information (Public File Activity)¥n").getBytes());
        } catch (FileNotFoundException e) {
            mFileView.setText(R.string.file_view);
        } catch (IOException e) {
            android.util.Log.e("PublicFileActivity", "failed to read file");
        } finally {
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    android.util.Log.e("PublicFileActivity", "failed to close file");
                }
            }
        }
        finish();
    }

    /**
    * Read file process
    *
    * @param view
    */
    public void onReadFileClick(View view) {
        FileInputStream fis = null;
        try {
            fis = openFileInput(FILE_NAME);
            byte[] data = new byte[(int) fis.getChannel().size()];
            fis.read(data);
            String str = new String(data);
            mFileView.setText(str);
        } catch (FileNotFoundException e) {
            mFileView.setText(R.string.file_view);
        } catch (IOException e) {
            android.util.Log.e("PublicFileActivity", "failed to read file");
        } finally {
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    android.util.Log.e("PublicFileActivity", "failed to close file");
                }
            }
        }
    }

    /**
    * Delete file process
    *
    * @param view
    */
    public void onDeleteFileClick(View view) {
        File file = new File(this.getFilesDir() + "/" + FILE_NAME);
        file.delete();
        mFileView.setText(R.string.file_view);
    }
}

PublicUserActivity.java

package org.jssec.android.file.publicuser.readonly;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;

public class PublicUserActivity extends Activity {

    private TextView mFileView;
    private static final String TARGET_PACKAGE = "org.jssec.android.file.publicfile.readonly";
    private static final String TARGET_CLASS = "org.jssec.android.file.publicfile.readonly.PublicFileActivity";
    private static final String FILE_NAME = "public_file.dat";

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.user);
        mFileView = (TextView) findViewById(R.id.file_view);
    }

    private void callFileActivity() {
        Intent intent = new Intent();
        intent.setClassName(TARGET_PACKAGE, TARGET_CLASS);
        try {
            startActivity(intent);
        } catch (ActivityNotFoundException e) {
            mFileView.setText("(File Activity does not exist)");
        }
    }

    /**
    * Call file Activity process
    *
    * @param view
    */
    public void onCallFileActivityClick(View view) {
        callFileActivity();
    }

    /**
    * Read file process
    *
    * @param view
    */
    public void onReadFileClick(View view) {
        FileInputStream fis = null;
        try {
            File file = new File(getFilesPath(FILE_NAME));
            fis = new FileInputStream(file);
            byte[] data = new byte[(int) fis.getChannel().size()];
            fis.read(data);
            // *** POINT 4 *** Regarding the information to be stored in files, handle file data carefully and securely.
            // Omitted, since this is a sample. Please refer to "3.2 Handling Input Data Carefully and Securely."
            String str = new String(data);
            mFileView.setText(str);
        } catch (FileNotFoundException e) {
            android.util.Log.e("PublicUserActivity", "no file");
        } catch (IOException e) {
            android.util.Log.e("PublicUserActivity", "failed to read file");
        } finally {
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    android.util.Log.e("PublicUserActivity", "failed to close file");
                }
            }
        }
    }

    /**
    * Rewrite file process
    *
    * @param view
    */
    public void onWriteFileClick(View view) {
        FileOutputStream fos = null;
        boolean exception = false;
        try {
            File file = new File(getFilesPath(FILE_NAME));
            // Fail to write in. FileNotFoundException occurs.
            fos = new FileOutputStream(file, true);
            fos.write(new String("Not sensitive information (Public User Activity)¥n").getBytes());
        } catch (IOException e) {
            mFileView.setText(e.getMessage());
            exception = true;
        } finally {
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    exception = true;
                }
            }
        }
        if (!exception)
            callFileActivity();
    }

    private String getFilesPath(String filename) {
        String path = "";
        try {
            Context ctx = createPackageContext(TARGET_PACKAGE,
            Context.CONTEXT_RESTRICTED);
            File file = new File(ctx.getFilesDir(), filename);
            path = file.getPath();
        } catch (NameNotFoundException e) {
            android.util.Log.e("PublicUserActivity", "no file");
        }
        return path;
    }
}

4.6.1.3 建立公共讀寫檔案

這是一種檔案用法,它允許未指定的大量應用的讀寫訪問。

未指定的大量應用可以讀寫,意思不用多說了。 惡意軟體也可以讀取和寫入,因此資料的可信度和安全性將永遠不會得到保證。 另外,即使在沒有惡意的情況下,也不能控制檔案中的資料格式或寫入的時間。 所以這種型別的檔案在功能方面幾乎不實用。

如上所述,從安全性和應用設計的角度來看,不可能安全地使用讀寫檔案,因此應該避免使用讀寫檔案。

要點:

不要建立允許來自其他應用的讀寫操作的檔案。

4.6.1.4 使用外部儲存器(公共讀寫)檔案

將檔案儲存在 SD 卡等外部儲存器中時,就是這種情況。當儲存比較龐大的資訊(放置從 Web 下載的檔案)或者將資訊帶出到外部時(備份等)時,應該使用它。

對於未指定的大量應用,“外部儲存器檔案(公共讀寫)”與“公共讀寫檔案“有相同特性。另外,對於宣告使用android.permission.WRITE_EXTERNAL_STORAGE許可權的應用,它和“公共讀寫檔案”具有相同的特性。因此,應儘可能減少“外部儲存器(公共讀寫)檔案”的使用。

按照 Android 應用的慣例,備份檔案很可能是在外部儲存器中建立的。但是,如上所述,外部儲存器中的檔案存在被其他應用(包括惡意軟體)篡改/刪除的風險。因此,在輸出備份的應用中,為了最小化應用規範或設計方面的風險,一些設計是必要的,例如顯示“儘快將備份檔案複製到 PC 等安全位置”。

要點:

1) 不得儲存敏感資訊。

2) 檔案必須儲存在每個應用的唯一目錄中。

3) 對於要儲存在檔案中的資訊,請仔細和安全地處理檔案資料。

4) 請求應用的檔案寫入應該按照規範禁止。

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="org.jssec.android.file.externalfile" >
    <!-- declare android.permission.WRITE_EXTERNAL_STORAGE permission to write to the external strage --
    >
    <!-- In Android 4.4 (API Level 19) and later, the application, which read/write only files in its sp
    ecific
    directories on external storage media, need not to require the permission and it should declare
    the maxSdkVersion -->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
    android:maxSdkVersion="18"/>
    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:allowBackup="false" >
        <activity
            android:name=".ExternalFileActivity"
            android:label="@string/app_name"
            android:exported="true" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

ExternalFileActivity.java

package org.jssec.android.file.externalfile;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;

public class ExternalFileActivity extends Activity {

    private TextView mFileView;
    private static final String TARGET_TYPE = "external";
    private static final String FILE_NAME = "external_file.dat";

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.file);
        mFileView = (TextView) findViewById(R.id.file_view);

    }
    /**
    * Create file process
    *
    * @param view
    */
    public void onCreateFileClick(View view) {
        FileOutputStream fos = null;
        try {
            // *** POINT 1 *** Sensitive information must not be stored.
            // *** POINT 2 *** Files must be stored in the unique directory per application.
            File file = new File(getExternalFilesDir(TARGET_TYPE), FILE_NAME);
            fos = new FileOutputStream(file, false);
            // *** POINT 3 *** Regarding the information to be stored in files, handle file data carefully and securely.
            // Omitted, since this is a sample. Please refer to "3.2 Handling Input Data Carefully and Securely."
            fos.write(new String("Non-Sensitive Information(ExternalFileActivity)¥n")
            .getBytes());
        } catch (FileNotFoundException e) {
            mFileView.setText(R.string.file_view);
        } catch (IOException e) {
            android.util.Log.e("ExternalFileActivity", "failed to read file");
        } finally {
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    android.util.Log.e("ExternalFileActivity", "failed to close file");
                }
            }
        }
        finish();
    }

    /**
    * Read file process
    *
    * @param view
    */
    public void onReadFileClick(View view) {
        FileInputStream fis = null;
        try {
            File file = new File(getExternalFilesDir(TARGET_TYPE), FILE_NAME);
            fis = new FileInputStream(file);
            byte[] data = new byte[(int) fis.getChannel().size()];
            fis.read(data);
            // *** POINT 3 *** Regarding the information to be stored in files, handle file data carefully and securely.
            // Omitted, since this is a sample. Please refer to "3.2 Handling Input Data Carefully and Securely."
            String str = new String(data);
            mFileView.setText(str);
        } catch (FileNotFoundException e) {
            mFileView.setText(R.string.file_view);
        } catch (IOException e) {
            android.util.Log.e("ExternalFileActivity", "failed to read file");
        } finally {
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    android.util.Log.e("ExternalFileActivity", "failed to close file");
                }
            }
        }
    }

    /**
    * Delete file process
    *
    * @param view
    */
    public void onDeleteFileClick(View view) {
        File file = new File(getExternalFilesDir(TARGET_TYPE), FILE_NAME);
        file.delete();
        mFileView.setText(R.string.file_view);
    }
}

使用的示例程式碼:

ExternalFileUser.java

package org.jssec.android.file.externaluser;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;

public class ExternalUserActivity extends Activity {

    private TextView mFileView;
    private static final String TARGET_PACKAGE = "org.jssec.android.file.externalfile";
    private static final String TARGET_CLASS = "org.jssec.android.file.externalfile.ExternalFileActivity";
    private static final String TARGET_TYPE = "external";
    private static final String FILE_NAME = "external_file.dat";

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.user);
        mFileView = (TextView) findViewById(R.id.file_view);
    }
    private void callFileActivity() {
        Intent intent = new Intent();
        intent.setClassName(TARGET_PACKAGE, TARGET_CLASS);
        try {
            startActivity(intent);
        } catch (ActivityNotFoundException e) {
            mFileView.setText("(File Activity does not exist)");
        }
    }

    /**
    * Call file Activity process
    *
    * @param view
    */
    public void onCallFileActivityClick(View view) {
        callFileActivity();
    }

    /**
    * Read file process
    *
    * @param view
    */
    public void onReadFileClick(View view) {
        FileInputStream fis = null;
        try {
            File file = new File(getFilesPath(FILE_NAME));
            fis = new FileInputStream(file);
            byte[] data = new byte[(int) fis.getChannel().size()];
            fis.read(data);
            // *** POINT 3 *** Regarding the information to be stored in files, handle file data carefully and securely.
            // Omitted, since this is a sample. Please refer to "3.2 Handling Input Data Carefully and Securely."
            String str = new String(data);
            mFileView.setText(str);
        } catch (FileNotFoundException e) {
            mFileView.setText(R.string.file_view);
        } catch (IOException e) {
            android.util.Log.e("ExternalUserActivity", "failed to read file");
        } finally {
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    android.util.Log.e("ExternalUserActivity", "failed to close file");
                }
            }
        }
    }

    /**
    * Rewrite file process
    *
    * @param view
    */
    public void onWriteFileClick(View view) {
        // *** POINT 4 *** Writing file by the requesting application should be prohibited as the specification.
        // Application should be designed supposing malicious application may overwrite or delete file.
        final AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this);
        alertDialogBuilder.setTitle("POINT 4");
        alertDialogBuilder.setMessage("Do not write in calling appllication.");
        alertDialogBuilder.setPositiveButton("OK",
        new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                callFileActivity();
            }
        });
        alertDialogBuilder.create().show();
    }

    private String getFilesPath(String filename) {
        String path = "";
        try {
            Context ctx = createPackageContext(TARGET_PACKAGE,
            Context.CONTEXT_IGNORE_SECURITY);
            File file = new File(ctx.getExternalFilesDir(TARGET_TYPE), filename);
            path = file.getPath();
        } catch (NameNotFoundException e) {
            android.util.Log.e("ExternalUserActivity", "no file");
        }
        return path;
    }
}

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="org.jssec.android.file.externaluser" >
    <!-- In Android 4.0.3 (API Level 14) and later, the permission for reading external storages
    has been defined and the application should decalre that it requires the permission.
    In fact in Android 4.4 (API Level 19) and later, that must be declared to read other directories
    than the package specific directories. -->
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:allowBackup="false" >
        <activity
            android:name=".ExternalUserActivity"
            android:label="@string/app_name"
            android:exported="true" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>


相關文章