4.3.1.1 创建/使用私有内容供应器

私有内容供应器是只由单一应用使用的内容提供者,它是最安全的内容供应器 [8]。

下面展示了如何实现私有内容供应器的示例代码。

要点(创建内容供应器):

  1. 将导出属性显式设置为false

  2. 即使数据来自相同应用,也应该小心并安全地处理收到的请求数据。

  3. 可以发送敏感信息,因为它在同一应用内发送和接收所有信息。

AndroidManifest.xml

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
  3. package="org.jssec.android.provider.privateprovider">
  4. <application
  5. android:icon="@drawable/ic_launcher"
  6. android:label="@string/app_name" >
  7. <activity
  8. android:name=".PrivateUserActivity"
  9. android:label="@string/app_name"
  10. android:exported="true" >
  11. <intent-filter>
  12. <action android:name="android.intent.action.MAIN" />
  13. <category android:name="android.intent.category.LAUNCHER" />
  14. </intent-filter>
  15. </activity>
  16. <!-- *** POINT 1 *** Explicitly set the exported attribute to false. -->
  17. <provider
  18. android:name=".PrivateProvider"
  19. android:authorities="org.jssec.android.provider.privateprovider"
  20. android:exported="false" />
  21. </application>
  22. </manifest>

PrivateProvider.java

  1. package org.jssec.android.provider.privateprovider;
  2. import android.content.ContentProvider;
  3. import android.content.ContentUris;
  4. import android.content.ContentValues;
  5. import android.content.UriMatcher;
  6. import android.database.Cursor;
  7. import android.database.MatrixCursor;
  8. import android.net.Uri;
  9. public class PrivateProvider extends ContentProvider {
  10. public static final String AUTHORITY = "org.jssec.android.provider.privateprovider";
  11. public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.org.jssec.contenttype";
  12. public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.org.jssec.contenttype";
  13. // Expose the interface that the Content Provider provides.
  14. public interface Download {
  15. public static final String PATH = "downloads";
  16. public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/" + PATH);
  17. }
  18. public interface Address {
  19. public static final String PATH = "addresses";
  20. public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/" + PATH);
  21. }
  22. // UriMatcher
  23. private static final int DOWNLOADS_CODE = 1;
  24. private static final int DOWNLOADS_ID_CODE = 2;
  25. private static final int ADDRESSES_CODE = 3;
  26. private static final int ADDRESSES_ID_CODE = 4;
  27. private static UriMatcher sUriMatcher;
  28. static {
  29. sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
  30. sUriMatcher.addURI(AUTHORITY, Download.PATH, DOWNLOADS_CODE);
  31. sUriMatcher.addURI(AUTHORITY, Download.PATH + "/#", DOWNLOADS_ID_CODE);
  32. sUriMatcher.addURI(AUTHORITY, Address.PATH, ADDRESSES_CODE);
  33. sUriMatcher.addURI(AUTHORITY, Address.PATH + "/#", ADDRESSES_ID_CODE);
  34. }
  35. // Since this is a sample program,
  36. // query method returns the following fixed result always without using database.
  37. private static MatrixCursor sAddressCursor = new MatrixCursor(new String[] { "_id", "city" });
  38. static {
  39. sAddressCursor.addRow(new String[] { "1", "New York" });
  40. sAddressCursor.addRow(new String[] { "2", "Longon" });
  41. sAddressCursor.addRow(new String[] { "3", "Paris" });
  42. }
  43. private static MatrixCursor sDownloadCursor = new MatrixCursor(new String[] { "_id", "path" });
  44. static {
  45. sDownloadCursor.addRow(new String[] { "1", "/sdcard/downloads/sample.jpg" });
  46. sDownloadCursor.addRow(new String[] { "2", "/sdcard/downloads/sample.txt" });
  47. }
  48. @Override
  49. public boolean onCreate() {
  50. return true;
  51. }
  52. @Override
  53. public String getType(Uri uri) {
  54. // *** POINT 2 *** Handle the received request data carefully and securely,
  55. // even though the data comes from the same application.
  56. // Here, whether uri is within expectations or not, is verified by UriMatcher#match() and switch case.
  57. // Checking for other parameters are omitted here, due to sample.
  58. // Please refer to "3.2 Handle Input Data Carefully and Securely."
  59. // *** POINT 3 *** Sensitive information can be sent since it is sending and receiving all within the same application.
  60. // However, the result of getType rarely has the sensitive meaning.
  61. switch (sUriMatcher.match(uri)) {
  62. case DOWNLOADS_CODE:
  63. case ADDRESSES_CODE:
  64. return CONTENT_TYPE;
  65. case DOWNLOADS_ID_CODE:
  66. case ADDRESSES_ID_CODE:
  67. return CONTENT_ITEM_TYPE;
  68. default:
  69. throw new IllegalArgumentException("Invalid URI:" + uri);
  70. }
  71. }
  72. @Override
  73. public Cursor query(Uri uri, String[] projection, String selection,
  74. String[] selectionArgs, String sortOrder) {
  75. // *** POINT 2 *** Handle the received request data carefully and securely,
  76. // even though the data comes from the same application.
  77. // Here, whether uri is within expectations or not, is verified by UriMatcher#match() and switch case.
  78. // Checking for other parameters are omitted here, due to sample.
  79. // Please refer to "3.2 Handle Input Data Carefully and Securely."
  80. // *** POINT 3 *** Sensitive information can be sent since it is sending and receiving all within the same application.
  81. // It depends on application whether the query result has sensitive meaning or not.
  82. switch (sUriMatcher.match(uri)) {
  83. case DOWNLOADS_CODE:
  84. case DOWNLOADS_ID_CODE:
  85. return sDownloadCursor;
  86. case ADDRESSES_CODE:
  87. case ADDRESSES_ID_CODE:
  88. return sAddressCursor;
  89. default:
  90. throw new IllegalArgumentException("Invalid URI:" + uri);
  91. }
  92. }
  93. @Override
  94. public Uri insert(Uri uri, ContentValues values) {
  95. // *** POINT 2 *** Handle the received request data carefully and securely,
  96. // even though the data comes from the same application.
  97. // Here, whether uri is within expectations or not, is verified by UriMatcher#match() and switch case.
  98. // Checking for other parameters are omitted here, due to sample.
  99. // Please refer to "3.2 Handle Input Data Carefully and Securely."
  100. // *** POINT 3 *** Sensitive information can be sent since it is sending and receiving all within the same application.
  101. // It depends on application whether the issued ID has sensitive meaning or not.
  102. switch (sUriMatcher.match(uri)) {
  103. case DOWNLOADS_CODE:
  104. return ContentUris.withAppendedId(Download.CONTENT_URI, 3);
  105. case ADDRESSES_CODE:
  106. return ContentUris.withAppendedId(Address.CONTENT_URI, 4);
  107. default:
  108. throw new IllegalArgumentException("Invalid URI:" + uri);
  109. }
  110. }
  111. @Override
  112. public int update(Uri uri, ContentValues values, String selection,
  113. String[] selectionArgs) {
  114. // *** POINT 2 *** Handle the received request data carefully and securely,
  115. // even though the data comes from the same application.
  116. // Here, whether uri is within expectations or not, is verified by UriMatcher#match() and switch case.
  117. // Checking for other parameters are omitted here, due to sample.
  118. // Please refer to "3.2 Handle Input Data Carefully and Securely."
  119. // *** POINT 3 *** Sensitive information can be sent since it is sending and receiving all within the same application.
  120. // It depends on application whether the number of updated records has sensitive meaning or not.
  121. switch (sUriMatcher.match(uri)) {
  122. case DOWNLOADS_CODE:
  123. return 5; // Return number of updated records
  124. case DOWNLOADS_ID_CODE:
  125. return 1;
  126. case ADDRESSES_CODE:
  127. return 15;
  128. case ADDRESSES_ID_CODE:
  129. return 1;
  130. default:
  131. throw new IllegalArgumentException("Invalid URI:" + uri);
  132. }
  133. }
  134. @Override
  135. public int delete(Uri uri, String selection, String[] selectionArgs) {
  136. // *** POINT 2 *** Handle the received request data carefully and securely,
  137. // even though the data comes from the same application.
  138. // Here, whether uri is within expectations or not, is verified by UriMatcher#match() and switch case.
  139. // Checking for other parameters are omitted here, due to sample.
  140. // Please refer to "3.2 Handle Input Data Carefully and Securely."
  141. // *** POINT 3 *** Sensitive information can be sent since it is sending and receiving all within the same application.
  142. // It depends on application whether the number of deleted records has sensitive meaning or not.
  143. switch (sUriMatcher.match(uri)) {
  144. case DOWNLOADS_CODE:
  145. return 10; // Return number of deleted records
  146. case DOWNLOADS_ID_CODE:
  147. return 1;
  148. case ADDRESSES_CODE:
  149. return 20;
  150. case ADDRESSES_ID_CODE:
  151. return 1;
  152. default:
  153. throw new IllegalArgumentException("Invalid URI:" + uri);
  154. }
  155. }
  156. }

下面是活动的示例,它使用私有内容供应器。

要点(使用内容供应器):

  1. 敏感信息可以发送,因为目标供应器在相同应用中。

  2. 小心和安全地处理收到的结果数据,即使数据来自相同应用。

PrivateUserActivity.java

  1. package org.jssec.android.provider.privateprovider;
  2. import android.app.Activity;
  3. import android.database.Cursor;
  4. import android.net.Uri;
  5. import android.os.Bundle;
  6. import android.view.View;
  7. import android.widget.TextView;
  8. public class PrivateUserActivity extends Activity {
  9. public void onQueryClick(View view) {
  10. logLine("[Query]");
  11. Cursor cursor = null;
  12. try {
  13. // *** POINT 4 *** Sensitive information can be sent since the destination provider is in the same application.
  14. cursor = getContentResolver().query(
  15. PrivateProvider.Download.CONTENT_URI, null, null, null, null);
  16. // *** POINT 5 *** Handle received result data carefully and securely,
  17. // even though the data comes from the same application.
  18. // Omitted, since this is a sample. Please refer to "3.2 Handling Input Data Carefully and Securely."
  19. if (cursor == null) {
  20. logLine(" null cursor");
  21. } else {
  22. boolean moved = cursor.moveToFirst();
  23. while (moved) {
  24. logLine(String.format(" %d, %s", cursor.getInt(0), cursor.getString(1)));
  25. moved = cursor.moveToNext();
  26. }
  27. }
  28. }
  29. finally {
  30. if (cursor != null) cursor.close();
  31. }
  32. }
  33. public void onInsertClick(View view) {
  34. logLine("[Insert]");
  35. // *** POINT 4 *** Sensitive information can be sent since the destination provider is in the same application.
  36. Uri uri = getContentResolver().insert(PrivateProvider.Download.CONTENT_URI, null);
  37. // *** POINT 5 *** Handle received result data carefully and securely,
  38. // even though the data comes from the same application.
  39. // Omitted, since this is a sample. Please refer to "3.2 Handling Input Data Carefully and Securely."
  40. logLine(" uri:" + uri);
  41. }
  42. public void onUpdateClick(View view) {
  43. logLine("[Update]");
  44. // *** POINT 4 *** Sensitive information can be sent since the destination provider is in the same application.
  45. int count = getContentResolver().update(PrivateProvider.Download.CONTENT_URI, null, null, null);
  46. // *** POINT 5 *** Handle received result data carefully and securely,
  47. // even though the data comes from the same application.
  48. // Omitted, since this is a sample. Please refer to "3.2 Handling Input Data Carefully and Securely."
  49. logLine(String.format(" %s records updated", count));
  50. }
  51. public void onDeleteClick(View view) {
  52. logLine("[Delete]");
  53. // *** POINT 4 *** Sensitive information can be sent since the destination provider is in the same application.
  54. int count = getContentResolver().delete(
  55. PrivateProvider.Download.CONTENT_URI, null, null);
  56. // *** POINT 5 *** Handle received result data carefully and securely,
  57. // even though the data comes from the same application.
  58. // Omitted, since this is a sample. Please refer to "3.2 Handling Input Data Carefully and Securely."
  59. logLine(String.format(" %s records deleted", count));
  60. }
  61. private TextView mLogView;
  62. @Override
  63. public void onCreate(Bundle savedInstanceState) {
  64. super.onCreate(savedInstanceState);
  65. setContentView(R.layout.main);
  66. mLogView = (TextView)findViewById(R.id.logview);
  67. }
  68. private void logLine(String line) {
  69. mLogView.append(line);
  70. mLogView.append("¥n");
  71. }
  72. }