Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,13 @@
android:label="@string/app_name"
android:theme="@style/AppTheme.AppCompat.Translucent">
</activity>

<!-- セキュリティ設定ダイアログ用Activity. -->
<activity
android:name="org.deviceconnect.android.manager.setting.SecuritySettingDialogActivity"
android:exported="false"
android:label="@string/app_name"
android:theme="@style/AppTheme.AppCompat.Translucent">
</activity>
<!-- キーワード表示用Activity. -->
<activity
android:name="org.deviceconnect.android.manager.setting.KeywordDialogActivity"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,34 @@
package org.deviceconnect.android.manager;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.SearchManager;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Intent;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.os.PowerManager;
import android.provider.MediaStore;
import android.provider.Settings;
import android.security.KeyChain;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.DialogFragment;

import android.util.Log;
import android.widget.Toast;

import org.deviceconnect.android.deviceplugin.host.HostDevicePlugin;
import org.deviceconnect.android.deviceplugin.host.activity.profile.CanvasProfileActivity;
import org.deviceconnect.android.deviceplugin.host.file.HostFileProvider;
import org.deviceconnect.android.localoauth.DevicePluginXmlUtil;
import org.deviceconnect.android.manager.core.DConnectManager;
import org.deviceconnect.android.manager.core.DConnectSettings;
Expand All @@ -31,19 +48,28 @@
import org.deviceconnect.android.manager.core.util.VersionName;
import org.deviceconnect.android.manager.profile.DConnectSettingProfile;
import org.deviceconnect.android.manager.setting.KeywordDialogActivity;
import org.deviceconnect.android.manager.setting.SecuritySettingDialogActivity;
import org.deviceconnect.android.manager.setting.SecuritySettingDialogFragment;
import org.deviceconnect.android.manager.setting.SettingActivity;
import org.deviceconnect.android.manager.util.NotificationUtil;
import org.deviceconnect.android.profile.SystemProfile;
import org.deviceconnect.android.provider.FileManager;
import org.deviceconnect.android.ssl.KeyStoreCallback;
import org.deviceconnect.android.ssl.KeyStoreError;
import org.deviceconnect.message.intent.message.IntentDConnectMessage;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
import java.util.Enumeration;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Level;
Expand Down Expand Up @@ -278,6 +304,7 @@ public void onError(final Exception e) {
}
}
}

/**
* Hostプラグインを追加します.
*/
Expand Down Expand Up @@ -427,7 +454,7 @@ public void openPluginSettings(final String pluginId) {
* プラグインの有効・無効を設定します.
*
* @param pluginId プラグインID
* @param enable 有効の場合はtrue、無効の場合はfalse
* @param enable 有効の場合はtrue、無効の場合はfalse
*/
public void setEnablePlugin(final String pluginId, final boolean enable) {
final DevicePlugin plugin = mManager.getPluginManager().getDevicePlugin(pluginId);
Expand Down Expand Up @@ -479,12 +506,21 @@ public void installRootCertificate() {
mManager.requestKeyStore(ipAddress, new KeyStoreCallback() {
@Override
public void onSuccess(final KeyStore keyStore, final Certificate cert, final Certificate rootCert) {

try {
Intent installIntent = KeyChain.createInstallIntent();
installIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
installIntent.putExtra(KeyChain.EXTRA_NAME, "Device Connect Root CA");
installIntent.putExtra(KeyChain.EXTRA_CERTIFICATE, rootCert.getEncoded());
startActivity(installIntent);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
Intent installIntent = new Intent();
installIntent.setClass(getApplicationContext(), SecuritySettingDialogActivity.class);
installIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
installIntent.putExtra(SecuritySettingDialogFragment.EXTRA_ROOT_CERT, rootCert.getEncoded());
startActivity(installIntent);
} else {
Intent installIntent = KeyChain.createInstallIntent();
installIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
installIntent.putExtra(KeyChain.EXTRA_NAME, "Device Connect Root CA");
installIntent.putExtra(KeyChain.EXTRA_CERTIFICATE, rootCert.getEncoded());
startActivity(installIntent);
}
} catch (Exception e) {
mLogger.log(Level.SEVERE, "Failed to encode server certificate.", e);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
SecuritySettingDialogActivity.java
Copyright (c) 2021 NTT DOCOMO,INC.
Released under the MIT license
http://opensource.org/licenses/mit-license.php
*/
package org.deviceconnect.android.manager.setting;

import android.content.Intent;
import android.os.Bundle;

import androidx.fragment.app.FragmentActivity;

/**
* セキュリティの設定画面を出すダイアログ表示用Activity.
* @author NTT DOCOMO, INC.
*/
public class SecuritySettingDialogActivity extends FragmentActivity {
@Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

Intent intent = getIntent();
if (intent == null) {
finish();
return;
}

byte[] rootCert = intent.getByteArrayExtra(SecuritySettingDialogFragment.EXTRA_ROOT_CERT);
if (rootCert == null) {
finish();
return;
}

SecuritySettingDialogFragment fragment = new SecuritySettingDialogFragment();
Bundle args = new Bundle();
args.putByteArray(SecuritySettingDialogFragment.EXTRA_ROOT_CERT, rootCert);
fragment.setArguments(args);
fragment.show(getSupportFragmentManager(), "security_setting_dialog");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
/*
SecuritySettingDialogFragment.java
Copyright (c) 2021 NTT DOCOMO,INC.
Released under the MIT license
http://opensource.org/licenses/mit-license.php
*/
package org.deviceconnect.android.manager.setting;

import android.app.Activity;
import android.app.Dialog;
import android.app.SearchManager;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.provider.Settings;
import android.util.Log;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.DialogFragment;

import org.deviceconnect.android.deviceplugin.host.file.HostFileProvider;
import org.deviceconnect.android.manager.R;
import org.deviceconnect.android.provider.FileManager;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

/**
* セキュリティ設定画面を開くダイアログ.
*
* @author NTT DOCOMO, INC.
*/
public class SecuritySettingDialogFragment extends DialogFragment {
private static final String TAG = "SecuritySettingDialogFragment";
public static final String EXTRA_ROOT_CERT = "root_cert";

@Override
public Dialog onCreateDialog(final Bundle savedInstanceState) {
Bundle args = getArguments();
if (args == null) {
dismiss();
}
byte[] rootCert = getArguments().getByteArray(EXTRA_ROOT_CERT);
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle(getString(R.string.dialog_title_security_setting));
builder.setMessage(getString(R.string.dialog_message_security_setting));
builder.setPositiveButton(R.string.dialog_open_security_setting, (dialog, which) -> {
final FileManager fileMgr = new FileManager(getContext(), HostFileProvider.class.getName());
fileMgr.saveFile("manager.pem", rootCert, true, new FileManager.SaveFileCallback() {
@Override
public void onSuccess(@NonNull String s) {
shareCA(new File(fileMgr.getBasePath(), "manager.pem"));
Intent installIntent = new Intent(Settings.ACTION_SECURITY_SETTINGS);
installIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
startActivity(installIntent);
}

@Override
public void onFail(@NonNull Throwable throwable) {
Toast.makeText(getContext(), R.string.dialog_error_message_not_export_ca, Toast.LENGTH_LONG).show();
}
});
});
builder.setNegativeButton(R.string.activity_launch_button_cancel, (dialog, which) -> {
Activity activity = getActivity();
if (activity != null) {
activity.finish();
}
});
return builder.create();
}

@Override
public void onStop() {
super.onStop();

Activity activity = getActivity();
if (activity != null) {
activity.finish();
}
}

private void shareCA(final File fileName) {
ContentResolver resolver = getContext().getContentResolver();
ContentValues values = new ContentValues();
values.put(MediaStore.Downloads.TITLE, fileName.getName());
values.put(MediaStore.Downloads.DISPLAY_NAME, fileName.getName());
values.put(MediaStore.Downloads.DATE_TAKEN, System.currentTimeMillis());
values.put(MediaStore.Downloads.MIME_TYPE, "application/x-pem-file");
values.put(MediaStore.Downloads.IS_PENDING, 1);
Uri uri = resolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, values);
if (uri == null) {
Log.e(TAG, "Failed to share ca: not inserted to ca store: path = " + fileName);
return;
}

try (InputStream in = new FileInputStream(fileName);
OutputStream out = resolver.openOutputStream(uri)) {
if (out == null) {
Log.e(TAG, "Failed to share photo: no output stream: path = " + fileName);
return;
}
byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
out.flush();
} catch (FileNotFoundException e) {
throw new IllegalStateException(e);
} catch (IOException e) {
Log.e(TAG, "Failed to share photo: I/O error: path = " + fileName, e);
return;
}

values.clear();
values.put(MediaStore.Downloads.IS_PENDING, 0);
resolver.update(uri, values, null, null);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -277,4 +277,10 @@
<string name="fragment_accesslog_delete_accesslog_positive">はい</string>
<string name="fragment_accesslog_delete_accesslog_negative">いいえ</string>
<string name="fragment_accesslog_empty">アクセスログがありません</string>

<!-- ================= Open Security Setting ================= -->
<string name="dialog_open_security_setting">セキュリティの設定を開く</string>
<string name="dialog_title_security_setting">証明書の手動インストール</string>
<string name="dialog_message_security_setting">Android11(R)以降、アプリケーションは認証局(CA)を自動的にインストールできなくなりました。\n\n設定で「セキュリティ>暗号化と視覚情報>証明書」のインストールの順に移動します。そこから「CA証明書」を選択肢新しくエクスポートした証明書ファイルを選択します。\n\n証明書は、外部ストレージのダウンロードフォルダにエクスポートしました。</string>
<string name="dialog_error_message_not_export_ca">CA証明書の出力に失敗しました</string>
</resources>
Original file line number Diff line number Diff line change
Expand Up @@ -277,4 +277,11 @@
<string name="fragment_accesslog_delete_accesslog_positive">Yes</string>
<string name="fragment_accesslog_delete_accesslog_negative">No</string>
<string name="fragment_accesslog_empty">No Access Log</string>

<!-- ================= Open Security Setting ================= -->
<string name="dialog_open_security_setting">Open the security settings</string>
<string name="dialog_title_security_setting">Manual installation of certificates</string>
<string name="dialog_message_security_setting">Starting with Android 11(R), applications can no longer automatically install a Certificate Authority (CA). \n\nIn the settings, go to \"Security>Encryption and Visual Information>Install Certificates\". From there, select \"CA Certificates\" and select the newly exported certificate file.\n\nWe exported the certificate to the Downloads folder on our external storage.</string>
<string name="dialog_error_message_not_export_ca">Failed to output CA certificate.</string>

</resources>